HashMap源码分析(四)
JDK1.8
从上几篇HashMap源码分析(一),HashMap源码分析(二),HashMap源码分析(三)我们了解了HashMap基本的数据结构,也了解了怎么生成链表的,也知道怎么动态扩容了,但这几片有几处我们当时是忽略的,比如当生成的链表个数大于8的时候。接下来我们就看一下大于8,具体是做哪些操作。
先看测试代码:
//为了测试,编写一个实体类,重写了hascode,所有的实例hashcode都是一样的
class Demo{
String name;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public Demo(String name) {
this.name = name;
}
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (!(o instanceof Demo)) return false;
Demo demo = (Demo) o;
return Objects.equals(getName(), demo.getName());
}
@Override
public int hashCode() {
return 123;
}
}
@Test
public void test(){
HashMap hashMap = new HashMap();
hashMap.put(new Demo("1"),"1");
hashMap.put(new Demo("2"),"1");
hashMap.put(new Demo("3"),"1");
hashMap.put(new Demo("4"),"1");
hashMap.put(new Demo("5"),"1");
hashMap.put(new Demo("6"),"1");
hashMap.put(new Demo("7"),"1");
hashMap.put(new Demo("8"),"1");
hashMap.put(new Demo("9"),"1");
}
前面8个put操作我们都分析过了,我们就看第九个操作,为什么是从第九个开始,看一下源码:
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 //TREEIFY_THRESHOLD
treeifyBin(tab, hash);
break;
}
if (e.hash == hash &&
((k = e.key) == key || (key != null && key.equals(k))))
break;
p = e;
}
可以看到当链表的长度大于8的时候(TREEIFY_THRESHOLD,从9的时候开始),开始调用**treeifyBin()**方法。
/** * Replaces all linked nodes in bin at index for given hash unless * table is too small, in which case resizes instead. 对于给定的哈希,在索引处替换bin中的所有链接节点,除非表太小,否则改为调整大小。 */
final void treeifyBin(Node<K,V>[] tab, int hash) {
int n, index; Node<K,V> e;
if (tab == null || (n = tab.length) < MIN_TREEIFY_CAPACITY) //64
resize();
else if ((e = tab[index = (n - 1) & hash]) != null) {
TreeNode<K,V> hd = null, tl = null;
do {
TreeNode<K,V> p = replacementTreeNode(e, null);
if (tl == null)
hd = p;
else {
p.prev = tl;
tl.next = p;
}
tl = p;
} while ((e = e.next) != null);
if ((tab[index] = hd) != null)
hd.treeify(tab);
}
}
从注释可以看出,在数组长度小于64的时候,就是相当于扩容。
下面我们为了测试,在初始化的时候传入数组长度。
HashMap hashMap = new HashMap(64);
下面通过图来讲解:
上图是没有转化时候的数据结构。
上面就是转化程红黑树的过程,调用的treefiyBin(),然后里面有调用了treefiy().
好吧,红黑树我就暂时分析到这,说实在,分析完我也不是太懂红黑树的原理。。。记住hashMap内部是数组+链表+红黑树组成的就行了。
在网上看到下图,总结的不错,分享一下,正好可以结合我之前的博客看一下。
(图片来源:https://blog.csdn.net/lianhuazy167/article/details/66967698)
HashMap jdk1.7和jdk1.8的区别
JDK1.7 | JDK1.8 | |
---|---|---|
扩容的顺序 | 先扩容,再插入 | 先插入,再扩容 |
扩容的机制 | 计算链表中每一个元素的下标 | 判断新增的bit位是否是1,是0就是原下标,是1就是原下标+原数组长度 |
红黑树 | 无 | 链表长度大于8是,转换成红黑树 |