HashMap链表长度大于8转红黑树

HashMap在JDK1.8中使用数组加链表或红黑树实现。当链表长度超过8(binCount=7,实际存储8个元素)且总元素数量大于64时,会将链表转换为红黑树,以优化查找性能。这个过程涉及到HashMap的扩容和treeifyBin方法。
摘要由CSDN通过智能技术生成

了解过HashMap的小伙伴都知道,JDK1.8时,HashMap采用数组+链表+红黑树存储

当单条链表长度超过8时,会将这条链表转换为红黑树,我们来分析一下源码看看

public class HashMap<K,V> ...{
    
    transient Node<K,V>[] table; //HashMap存储数据的容器,也称为Hash桶
    
    //The bin count threshold for using a tree rather than list for a bin.
    //使用树而不是列表作为存储箱的存储箱计数阈值。即链表转为红黑树
    static final int TREEIFY_THRESHOLD = 8;
    
     
    //The default initial capacity - MUST be a power of two.
    //默认的初始容量-必须是二的幂。
    static final int DEFAULT_INITIAL_CAPACITY = 1 << 4; // aka 16 位移运算 = 10000 = 16
    
    //The number of key-value mappings contained in this map.
    //此映射中包含的键值映射的数量。
    transient int size;
    
    //The load factor used when none specified in constructor.
    //在构造函数中未指定时使用的负载系数。
    static final float DEFAULT_LOAD_FACTOR = 0.75f;
    
    //The next size value at which to resize (capacity * load factor).
    //要调整大小的下一个大小值(容量*负载系数)。即扩容阈值
    int threshold;
    
    public V put(K key, V value) {
        return putVal(hash(key), key, value,...); //hash值8位16进制
    }
    
	final V putVal(int hash, K key, V value,...) {
    	Node<K,V>[] tab; //hashMap存储数的容器
    	Node<K,V> p; //数据以Node格式存储
    	int n, i; 
    
        //tab hash表 n=hash表长度(第一次添加元素为16)
    	if ((tab = table) == null || (n = tab.length) == 0){
       	 n = (tab = resize()).length; //如果第一次添加元素,扩容hash表
    	}
        //p=当前元素的存储下标里的第一个元素 i=当前元素存储下标
        if ((p = tab[i = (n - 1) & hash]) == null){
            tab[i] = newNode(hash, key, value, null); //计算出当前元素存储位置,判断位置是否为空,空则直接存入
        }else{
            //执行到这里代表hash表不为空,且当前元素存储的hash表下标也不为空
            
            //判断当前元素存储的下标的第一个元素hash值 是否与当前元素的hash值相等 && 值是否相等
            if (p.hash == hash &&
                ((k = p.key) == key || (key != null && key.equals(k)))){
                e = p; //相等则直接覆盖值,添加操作完成
            }
            ...
            else {
                //执行到这里代表当前元素存储的hash表下标不为空,且第一个元素不等于当前存入元素
                //+1
                for (int binCount = 0; ; ++binCount) {
                    //e= p的下一个指向的元素
                    if ((e = p.next) == null) {
                        //如果p的下一个元素为空则直接把元素存入到p的下一个元素
                        p.next = newNode(hash, key, value, null); //+1
                        if (binCount >= TREEIFY_THRESHOLD - 1){
                            //binCount+1+1 => 链表长度>TREEIFY_THRESHOLD(8)
                            //当前下标存储元素超过8个之后,将当前下标的元素存储结构由单向链表转为红黑树
                            treeifyBin(tab, hash);
                        }                            
                        break;
                    }
                    if (e.hash == hash &&
                        ((k = e.key) == key || (key != null && key.equals(k)))){
                        //p的下一个元素不为空,并且hash值一致,值也一致则跳出循环
                        break;
                    }                        
                    p = e;
                } 
                
            
        }
        if (++size > threshold){
            //当前存储元素的总数量>负载阈值,则触发Hash表扩容
            resize();  
        }
	}
        
        //The smallest table capacity for which bins may be treeified
        //可以将存储箱树化的最小表容量。
       static final int MIN_TREEIFY_CAPACITY = 64; 
     
      /*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) {
            ... //链表转为红黑树操作略
        }
    }
            
}            

截取部分源码:

//使用树而不是列表作为存储箱的存储箱计数阈值。即链表转为红黑树
 static final int TREEIFY_THRESHOLD = 8;

看到这里有人可能会奇怪TREEIFY_THRESHOLD不是=8吗,条件既然是binCount>=8-1,那么binCount应该>6时就会转换了呀,为什么都说链表大于8时才会转换红黑树呢?

注意:binCount初始为0,所以当binCount=7时,实际已经存储了8个元素,此时条件binCount>=8-1代表binCount>7时会转换,接着继续分析:

注意看:

在判断之前还进行了元素添加操作,所以此时的数组存储实际元素数量应为:binCount+2

也就是binCount>=8-1代表binCount>8时会转换

接下来继续看treeifyBin(tab,hash)方法

 

 只有当整个HashMap存储的元素总数!<64时才会转换红黑树,否则会改成数组扩容

总结:当链表长度>8且总元素数量大于64时会将链表转化为红黑树

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值