JDK1.8引入了红黑树,当链表的长度大于8,且数组的大少大于等于64时,链表会转换为红黑树。 当红黑树的结点个数小于6时,会转换为链表 HashMap底层主要代码:
transient Entry[] table;从代码中可以看到一个hashmap的主干就是一个数组结构,当新建一个hashmap的时候,就会初始化一个数组。
static class Entry<K,V> implements Map.Entry<K,V> { final K key; V value; final int hash; Entry<K,V> next; .......... }Entry就是数组中的元素,它持有一个指向下一个元素的引用,这就构成了链表。 往hashmap中put元素的时候,先根据key的hash值得到这个元素在数组中的位置(即下标),然后就可以把这个元素放到对应的位置中了。如果这个元素所在的位子上已经存放有其他元素了,那么在同一个位子上的元素将以链表的形式存放,新加入的放在链头,最先加入的放在链尾。从hashmap中get元素时,首先计算key的hashcode,找到数组中对应位置的某一元素,然后通过key的equals方法在对应位置的链表中找到需要的元素。从这里我们可以想象得到,如果每个位置上的链表只有一个元素,那么hashmap的get效率将是最高的
HashMap使用的是懒加载,构造完HashMap对象后,只要不进行put 方法插入元素之前,HashMap并不会去初始化或者扩容table。 当首次调用put方法时,HashMap会发现table为空然后调用resize方法进行初始化,当添加完元素后,如果HashMap发现size(元素总数)大于threshold(阈值),则会调用resize方法进行扩容
简单来说: 扩容,即是创建一个新的Entry空数组,长度是原数组的2倍。然后遍历原Entry数组,把所有的Entry重新Hash到新数组
重新哈希的原因: Hash的公式:index = HashCode(Key) & (Length - 1) 因为数组的长度发生了改变
插入流程:
判断数组是否为空,为空则进行初始化不为空,计算 k 的 hash 值,通过 (n - 1) & hash 计算应当存放在数组中的下标 index判断计算出的 index 处是否已存在数据,没有存在数据,则直接插入该数据。如果已经存在数据,则说明发生了哈希冲突,继续进行下一步判断要插入的数据的 Key 和已经存在的数据的 Key 是否相等,如果相等,则直接把要插入的数据的 value 覆盖已存在的数据的 value 即可如果不相等,则需要判断当前的数据结点类型是链表类型还是树型结点。如果是树型结点,则把该数据插入红黑树的叶子结点即可如果该结点是链表类型,则创建一个普通 Node 加入链表中。插入完成后,判断链表长度是否大于8,且数组长度大于64,满足的话则转换成红黑树插入完成后,判断当前结点树是否大于阈值,大于的话则开始扩容,扩容为原数组的2倍图片来源:blog.csdn.net/zhengwangzw
