Map接口
双列集合,用来存储一对(key - value)一对的数据
Map接口实现类体系结构
HashMap
哈希表(hash table)
相当于是一个书架,其实是数组+链表
HashMap底层
数组+链表 (JDK7及之前),数组+链表+红黑树(JDK8)
HashMap源码分析
HashMap中的几个属性
① DEFAULT_INITIAL_CAPACITY : HashMap的默认容量,16
② DEFAULT_LOAD_FACTOR:HashMap的默认加载因子:0.75
③ threshold:扩容的临界值 = 容量*填充因子:16 * 0.75 = 12,调用HashMap的空参构造器, 默认扩容的临界值为12
④ TREEIFY_THRESHOLD:Bucket中链表长度大于该默认值,转化为红黑树:8
⑤ MIN_TREEIFY_CAPACITY:桶中的Node被树化时最小的hash表容量:64
源码
HashMap map = new HashMap(); 在实例化以后,首次调用put()底层创建了长度是16的一维数组Node[] table
补充
JDK8相较于JDK7在底层实现方面不同:
① new HashMap():底层没有创建一个长度为16的数组
② JDK8底层的数组是Node[],而非Entry[]
③ 首次调用put()时,底层创建长度为16的数组
V put(K key, V value)
判断hashtable是否存在,如果不存在则创建
if ((tab = table) == null || (n = tab.length) == 0)
n = (tab = resize()).length;
判断当前数组下标对应的元素是否为null,如果是则创建一个Node对象放入
if ((p = tab[i = (n - 1) & hash]) == null)
tab[i] = newNode(hash, key, value, null);
如果传入的key在哈希表中存在的话,进行数据的替换
if (p.hash == hash &&
((k = p.key) == key || (key != null && key.equals(k))))
e = p;
判断当前节点是否是红黑树
else if (p instanceof TreeNode)
如果链表的长度大于8并且数组长度大于64时,会将链表转成红黑树
补充
因为binCount从0开始,所以binCount >= 7即链表长度大于8时进入treeifyBin(tab, hash)
for (int binCount = 0; ; ++binCount) {//binCount记录哈希桶中链表的长度
if ((e = p.next) == null) {
p.next = newNode(hash, key, value, null);
if (binCount >= TREEIFY_THRESHOLD - 1) //TREEIFY_THRESHOLD = 8,
treeifyBin(tab, hash);
break;
}
...代码省略
在treeifyBin(tab, hash)中哈希桶的长度小于64就扩容,并不是链表长度大于8就变成红黑树
所以链表的长度大于8并且哈希桶长度大于64时,才会将链表转成红黑树
if (tab == null || (n = tab.length) < MIN_TREEIFY_CAPACITY)//MIN_TREEIFY_CAPACITY = 64
resize();
为什么初始长度是2的n次幂
为了保证根据hash计算出索引位置的正确性,底层使用的是 & 位与运算计算出数据在哈希桶的索引位置
HashMap map = new HashMap(17);
当手动设置Hash Map初始长度不是2的n次幂,调用tableSizeFor(int cap)计算出比传入参数(cap)大的且离他最近的2的n次幂的值,并将该值赋给threshold
static final int tableSizeFor(int cap) {
int n = cap - 1;
n |= n >>> 1;
n |= n >>> 2;
n |= n >>> 4;
n |= n >>> 8;
n |= n >>> 16;
return (n < 0) ? 1 : (n >= MAXIMUM_CAPACITY) ? MAXIMUM_CAPACITY : n + 1;
}
例如:
如果传入17,则返回32,如果传入13,会返回16
结语
笔者处于学习阶段,并没有过多深入探究源码(主要是看不懂),该文章如果哪里有论述不是很清楚的或者是写错的还望大佬们纠正,万分感谢。