Java基础内容之再也不怕被HashMap欺负了

简介: 1. 判断 table 是否为 null。为 null 则新建一个 table 数组2. 调用 hash 获取 该 key 的 hash 值3. 将hash & n-1的值当做下标去找数据4. 如果发现有数据 1. 但是数据的hash和key都和当前要插入的一致就替换。(此时还是一个Node节点) 2. 但是数据的hash一致,但是key不一致,说明是hash冲突了。就转换成一个Node链表,数据放到链表尾部5. 如果发现链表长度大于等于8,就转换成红黑

作者: 西魏陶渊明
博客: https://blog.springlearn.cn/

HashMap是我们在日常开发中经常使用的一个结合类型,同时也是面试时候最好提问的集合类型,在这里进行整理
一起学习,进步。

一、数据结构

先说两种数据结构, 不用怕, 如果要对付面试只要了解就行了。不用手写实现, 同时也因为已经有人帮我写好,所以开发中我们只要用就行。

1. 二叉树

动画展示二叉树

本来是一个相对平衡的二叉树(当前数据 > 根节点 ? 从右边插入 : 从左边插入)。

但是由于在使用的过程中的删除,慢慢的变成了一个瘸腿。此时树的高度越高,数据越多,导致查询叶子
的耗时越长。

于是乎人们在这个数据结构的基础上,研究出新的结构,就是下面的红黑树。

2. 红黑树

动画展示红黑树

依次插入7 5 3 2 4 6 8 9 12 11 17 13 14 16

很明显我们可以看出红黑树比二叉树相对比较平衡。

在对比一下二叉树

好了关于数据结构的知识就说这么多,可以通过图就能知道这两种数据结构情况了。因为数据结构不是我们本篇研究的点。
所以就提这么多。

二、源码分析

HashMap 实现了 Map 接口,JDK1.7由 数组 + 链表实现, 1.8后由 数组 + 链表 + 红黑树实现

1. put的源码分析

HashMap中声明的常量信息,注意看。下面源码中会提到。

变量 含义
DEFAULT_INITIAL_CAPACITY 默认的初始容量
MAXIMUM_CAPACITY 最大的容量2^30
DEFAULT_LOAD_FACTOR 容器个数 size > 负载因子 * 数组长度 就需要进行扩容
TREEIFY_THRESHOLD 如果数组中某一个链表 >= 8 需要转化为红黑树
UNTREEIFY_THRESHOLD 如果数组中某一个链表转化为红黑树后的节点 < 6 的时候 继续转为 链表
 final V putVal(int hash, K key, V value, boolean onlyIfAbsent,
                   boolean evict) {
        Node<K,V>[] tab; Node<K,V> p; int n, i;
        if ((tab = table) == null || (n = tab.length) == 0)
            n = (tab = resize()).length;
        if ((p = tab[i = (n - 1) & hash]) == null)
            tab[i] = newNode(hash, key, value, null);
        else {
            Node<K,V> e; K k;
            if (p.hash == hash &&
                ((k = p.key) == key || (key != null && key.equals(k))))
                e = p;
            //判断是否是树    
            else if (p instanceof TreeNode)
                e = ((TreeNode<K,V>)p).putTreeVal(this, tab, hash, key, value);
            //继续用链表    
            else {
                // 循环链表
                for (int binCount = 0; ; ++binCount) {
                    if ((e = p.next) == null) {
                        // 新建节点存储
                        p.next = newNode(hash, key, value, null);
                        if (binCount >= TREEIFY_THRESHOLD - 1) // -1 for 1st
                            //大于树的阀值,就转换为树结构
                            treeifyBin(tab, hash);
                        break;
                    }
                    if (e.hash == hash &&
                        ((k = e.key) == key || (key != null && key.equals(k))))
                        break;
                    p = e;
                }
            }
            if (e != null) { // existing mapping for key
                V oldValue = e.value;
                if (!onlyIfAbsent || oldValue == null)
                    e.value = value;
                afterNodeAccess(e);
                return oldValue;
            }
        }
        ++modCount;
        if (++size > threshold)
            resize();
        afterNodeInsertion(evict);
        return null;
    }

从上面源码中我们可以看到在put时候会判断是链表结构还是红黑树。如果是树就用树put
((TreeNode<K,V>)p).putTreeVal(this, tab, hash, key, value);

如果是链表就循环列表插入数据,如果发现列表长度大于树的阀值就讲链表转换为树

2. put流程赘述

  1. 判断 table 是否为 null。为 null 则新建一个 table 数组
  2. 调用 hash 获取 该 key 的 hash 值

  1. 将hash & n-1的值当做下标去找数据
  2. 如果发现有数据

    1. 但是数据的hash和key都和当前要插入的一致就替换。(此时还是一个Node节点)
    2. 但是数据的hash一致,但是key不一致,说明是hash冲突了。就转换成一个Node链表,数据放到链表尾部
  3. 如果发现链表长度大于等于8,就转换成红黑树

三、面试知识扩展

前面我们知道了HashMap在1.8之后的优化。这里我们最后再说一个面试题。
问: 1.7时候hashmap在扩容时候回出现死链的问题。问题原因是什么? 已经出现的场景是什么?

首先看下扩容方法 resize

1. 优化1

jdk1.8在对链表进行扩容时候时候不是直接都去hash了。而是
(e.hash & oldCap) == 0 下标不变
(e.hash & oldCap) != 0 下标 = 原下标 + oldCap

2. 出现的场景

多线程操作扩容

最后求关注,求订阅,谢谢你的阅读!

相关文章
|
20天前
|
Java
Java之HashMap详解
本文介绍了Java中HashMap的源码实现(基于JDK 1.8)。HashMap是基于哈希表的Map接口实现,允许空值和空键,不同步且线程不安全。文章详细解析了HashMap的数据结构、主要方法(如初始化、put、get、resize等)的实现,以及树化和反树化的机制。此外,还对比了JDK 7和JDK 8中HashMap的主要差异,并提供了使用HashMap时的一些注意事项。
Java之HashMap详解
|
2月前
|
存储 Java
Java中的HashMap和TreeMap,通过具体示例展示了它们在处理复杂数据结构问题时的应用。
【10月更文挑战第19天】本文详细介绍了Java中的HashMap和TreeMap,通过具体示例展示了它们在处理复杂数据结构问题时的应用。HashMap以其高效的插入、查找和删除操作著称,而TreeMap则擅长于保持元素的自然排序或自定义排序,两者各具优势,适用于不同的开发场景。
46 1
|
2月前
|
存储 安全 Java
Java Map新玩法:探索HashMap和TreeMap的高级特性,让你的代码更强大!
【10月更文挑战第17天】Java Map新玩法:探索HashMap和TreeMap的高级特性,让你的代码更强大!
65 2
|
2月前
|
存储 Java 开发者
Java Map实战:用HashMap和TreeMap轻松解决复杂数据结构问题!
【10月更文挑战第17天】本文深入探讨了Java中HashMap和TreeMap两种Map类型的特性和应用场景。HashMap基于哈希表实现,支持高效的数据操作且允许键值为null;TreeMap基于红黑树实现,支持自然排序或自定义排序,确保元素有序。文章通过具体示例展示了两者的实战应用,帮助开发者根据实际需求选择合适的数据结构,提高开发效率。
64 2
|
2月前
|
存储 缓存 安全
HashMap VS TreeMap:谁才是Java Map界的王者?
HashMap VS TreeMap:谁才是Java Map界的王者?
77 2
|
2月前
|
存储 缓存 Java
java基础:IO流 理论与代码示例(详解、idea设置统一utf-8编码问题)
这篇文章详细介绍了Java中的IO流,包括字符与字节的概念、编码格式、File类的使用、IO流的分类和原理,以及通过代码示例展示了各种流的应用,如节点流、处理流、缓存流、转换流、对象流和随机访问文件流。同时,还探讨了IDEA中设置项目编码格式的方法,以及如何处理序列化和反序列化问题。
82 1
java基础:IO流 理论与代码示例(详解、idea设置统一utf-8编码问题)
|
2月前
|
存储 Java 程序员
Java面试加分点!一文读懂HashMap底层实现与扩容机制
本文详细解析了Java中经典的HashMap数据结构,包括其底层实现、扩容机制、put和查找过程、哈希函数以及JDK 1.7与1.8的差异。通过数组、链表和红黑树的组合,HashMap实现了高效的键值对存储与检索。文章还介绍了HashMap在不同版本中的优化,帮助读者更好地理解和应用这一重要工具。
55 5
|
2月前
|
Java
用java搞定时任务,将hashmap里面的值存到文件里面去
本文介绍了如何使用Java的`Timer`和`TimerTask`类创建一个定时任务,将HashMap中的键值对写入到文本文件中,并提供了完整的示例代码。
38 1
用java搞定时任务,将hashmap里面的值存到文件里面去
|
2月前
|
存储 Java API
详细解析HashMap、TreeMap、LinkedHashMap等实现类,帮助您更好地理解和应用Java Map。
【10月更文挑战第19天】深入剖析Java Map:不仅是高效存储键值对的数据结构,更是展现设计艺术的典范。本文从基本概念、设计艺术和使用技巧三个方面,详细解析HashMap、TreeMap、LinkedHashMap等实现类,帮助您更好地理解和应用Java Map。
55 3
|
2月前
|
存储 缓存 安全
在Java的Map家族中,HashMap和TreeMap各具特色
【10月更文挑战第19天】在Java的Map家族中,HashMap和TreeMap各具特色。HashMap基于哈希表实现,提供O(1)时间复杂度的高效操作,适合性能要求高的场景;TreeMap基于红黑树,提供O(log n)时间复杂度的有序操作,适合需要排序和范围查询的场景。两者在不同需求下各有优势,选择时需根据具体应用场景权衡。
33 2