成员变量:
1、序列化版本号:
private static final long serialVersionUID = 362498820763181265L;
2、集合的初始化容量:(默认为16)
/**
* The default initial capacity - MUST be a power of two.
*/
//默认为16:1<<4(左移4位)相当于1*24次方
static final int DEFAULT_INITIAL_CAPACITY = 1 << 4; // aka 16
问题1:初始化容量为什么是2的n次幂?如果不为2的n次幂会怎么样?
当向HashMap中添加元素的时候,需要根据key的hash值去确定其在数组中的位置,HashMap为了存取高效,尽量减少hash碰撞,就需要尽量把数据分配均匀,每个链表(也可能位红黑树)的长度大致相同。这就需要算法:
这个算法实际上就是取模,hash%length,但是计算机中直接求余的效率不如位运算。所以源码中做了优化,使用:hash&(length-1)按位与,实际上hash%length等于hash&(length-1),前提是length是2的n次幂。
但是为什么这样能够均匀分布减少hash碰撞呢?2的n次幂-1实际上就是n个1.
比如:
为2的n次幂:
不为2的n次幂:
图片.png
可以发现:如果数组的长度不是2的n次幂,计算出的索引值很特别容易相同(发生hash碰撞),会导致数组的每个位置下的数据量不均匀,个别位置的链表或者红黑树过长,导致效率降低。
问题2:当初始化HashMap的时候指定了容量(比如为10),会怎么样?
HashMap<String,Integer> hashMap = new HashMap<>(10);
根据源码可以发现:如果初始化给的容量不是2的n次幂,最后容量会变成比初始化给定的值大的最小的2的n次幂。
比如给定的10,将会变成16;给定的17,将会变成32
public static void main(String[] args) throws Exception {
HashMap<String,Integer> hashMap = new HashMap<>(11);
Class aClass = hashMap.getClass();
Method capacity = aClass.getDeclaredMethod("capacity");
capacity.setAccessible(true);
System.out.println(capacity.invoke(hashMap));
HashMap<String,Integer> hashMap2 = new HashMap<>(17);
Class aClass2 = hashMap.getClass();
Method capacity2 = aClass2.getDeclaredMethod("capacity");
capacity2.setAccessible(true);
System.out.println(capacity2.invoke(hashMap2));
}
********************************结果:********************************
16
32
3、负载因子,默认为0.75:
static final float DEFAULT_LOAD_FACTOR = 0.75f;
默认容量为16,当容量超过16*0.75=12时会自动扩容
4、集合最大容量(1<<30)2的30次幂:
static final int MAXIMUM_CAPACITY = 1 << 30;
5、当链表的的数量超过8,会转为红黑树:(并且数组的长度大于64,也就是参数7)
static final int TREEIFY_THRESHOLD = 8;
6、当红黑树的数量小于6,会从红黑树转为链表:
static final int UNTREEIFY_THRESHOLD = 6;
7、链表转为红黑树对应的数组长度的最小值:
static final int MIN_TREEIFY_CAPACITY = 64;
网友评论