hashMap扩充机制总结

了解基本的概念和术语:

数组链表结合体:每一个hashMap都是一个数组链表结合体,每一个数组的元素是一条链表
桶:约定数组的每一个格元素称为桶

bin:约定桶后面存放的每一个数据称为bin

size:表示HashMap中存放KV的数量(为链表和树中的KV的总和)。transient int size;这个size不可能大于capcity,因为一旦大于阈值就扩充resize
transient(瞬变的)关键字,如果一个类的某个成员确定不被序列化就加transient,防止被序列化

capcity:指HashMap中桶的数量。默认值为16。一般第一次扩容时会扩容到64,之后好像是2倍。总之,容量都是2的幂。(下面的容量都是指数组的长度),这个数组的容量是不可能填满的,因为达到阈值就会被扩充,因为有很多的hashCode进行与运算后的结果是一样的,都是最追加到同一条链表上

加载因子load_Factor: 用于判断什么时候进行hashMap的扩容,其实就是增加数组的长度;当原来的数组长度0.75被赋值了之后就进行扩容(这里指的不是hash表啊,是数组长度)HashMap默认的加载因子是0.75,最大容量是16,因此可以得出HashMap的默认容量是:0.7516=12。(扩容的实质就是增加数组的长度)

阈值threshold:capcity*load_factory

hash碰撞:用put到这个hashMap的元素的Key的hashCode来和数组的长度进行“与”运算,与与运算的结果决定了它追加到哪一个数组元素的链表上,当两个hashMap元素的key与运算完了之后有相同的与结果就是hash碰撞(即会追加到相同的链表上)
树形化:hashMap的数组元素,也就是链表长度达到8就将链表树形化(变为红黑树)。这时候的元素不再是一个链表而是一个红黑树

hashMap的3个构造器:

加载因子是表示Hsah表中元素的填满的程度.若:加载因子越大,填满的元素越多,好处是,空间利用率高了,但:冲突的机会加大了.反之,加载因子越小,填满的元素越少,好处是:冲突的机会减小了,但:空间浪费多了

HashMap():构建一个初始容量为 16,负载因子为 0.75 的 HashMap。

HashMap(int initialCapacity):构建一个初始容量为 initialCapacity,负载因子为 0.75 的 HashMap。

HashMap(int initialCapacity, float loadFactor):以指定初始容量、指定的负载因子创建一个 HashMap。

hashMap的数组长度为什么默认是16(2的4次方)

hashMap的默认的数组长度是16,每一次扩容的时候也是扩充到2的n次方
看下图:左边表示数组长度是16的,右边的是数组长度是15
hashMap扩充机制总结
上图很好的解析了hashMap每一次put加入一个元素的过程:每一次加入一个元素的hashCode值都会和数组长度的值的二进制数进行与运算,右边的不同的两个数与出来的结果是相同的,那就是造成hash碰撞,更加惨的是与出来的结果最后一位永远是0,从而造成数组下标的二进制值的最后一位是1的数组元素永远没有存到数据,造成极大的空间浪费,例如0001(数组下标为1),0011(数组下标为3),0101(数组下标为3)等等。。所以数组的长度必须是2的n次方,每一位都是1,才能减少空间的浪费。

什么时候进行扩容resize().本质就是修改数组的长度和hash表的容量(when:size>=threshold(capcity*loadFactor)

当hashmap中的元素越来越多的时候,碰撞的几率也就越来越高(因为数组的长度是固定的),所以为了提高查询的效率,就要对hashmap的数组进行扩容,数组扩容这个操作也会出现在ArrayList中,所以这是一个通用的操作,数组的扩容都是新建一个数组来存储原来数组的元素,因为数组的长度是不可变的。。很多人对它的性能表示过怀疑,不过想想我们的“均摊”原理,就释然了,而在hashmap数组扩容之后,最消耗性能的点就出现了:原数组中的数据必须重新计算其在新数组中的位置,并放进去,这就是resize。
那么hashmap什么时候进行扩容呢?
当hashmap中的元素个数超过数组大小loadFactor时,就会进行数组扩容,loadFactor的默认值为0.75,也就是说,默认情况下,数组大小为16,那么当hashmap中元素个数超过160.75=12的时候,就把数组的大小扩展为216=32,即扩大一倍,然后重新计算每个元素在数组中的位置,而这是一个非常消耗性能的操作,所以如果我们已经预知hashmap中元素的个数,那么预设元素的个数能够有效的提高hashmap的性能。比如说,我们有1000个元素new HashMap(1000), 但是理论上来讲new HashMap(1024)更合适,不过上面annegu已经说过,即使是1000,hashmap也自动会将其设置为1024。 但是new HashMap(1024)还不是更合适的,因为0.751000 < 1000, 也就是说为了让0.75 * size > 1000, 我们必须这样new HashMap(2048)才最合适,既考虑了&的问题,也避免了resize的问题。

##每个桶后面的所有bin 什么时候时候树形化
必须满足两个条件:
1:每个桶后面的bin的个数超过或者等于static final int TREEIFY_THRESHOLD = 8
2:容量capcity超过 static final int MIN_TREEIFY_CAPACITY = 64
一旦一个hashmap加入的元素满足这两个条件就会发生bin树形化,不用链表存储,用红黑树存储

两个条件调试参考