HashMap中的put源码分析

本文详细分析了HashMap的put方法,从hash()函数开始,解释了哈希值的计算过程,探讨了位异或运算在散列分布中的作用。接着,文章详述了putVal()方法内部的逻辑,包括链表和红黑树的插入操作,以及何时进行扩容和树化。通过对源码的解读,揭示了HashMap在处理冲突和保证性能方面的策略。

HashMap put方法分析

public V put(K key, V value) {
    return putVal(hash(key), key, value, false, true);
}

hash():hash扰动函数

static final int hash(Object key) {
    int h;
    return (key == null) ? 0 : (h = key.hashCode()) ^ (h >>> 16);
}

如果key为空的话,直接返回0(存放位置的index也为0。因为Node.hash0,通过路由寻址公式:(tabel.length - 1) & node.hash,无论与什么数相与,最后的结果肯定都为0,相反,如果在index = 0的位置上存在一个Node,那么该Node.hash一定是0);若不是,先调用key的hashCode方法算出key的哈希值。^(位异或运算,相异为1,相同为0)。h向右无符号右移16位,前边补零。之后再和原来的哈希值进行位异或运算。目的就是为了让高位也参与路由运算。(路由寻址公式:(tabel.length - 1) & node.hash

为什么要用异或运算?

根据图中可知,异或运算中的0和1比例是1:1出现的。所以用位异或运算更可以让数据更加随机,更加散列。

HashMap中put方法详解:

final V putVal(int hash, K key, V value, boolean onlyIfAbsent, boolean evict) {
    //tab:引用当前hashMap的散列表
    //p:表示当前散列表的元素
    //n:表示散列表数组的长度
    //i:表示路由寻址的结果
    HashMap.Node<K,V>[] tab; HashMap.Node<K,V> p; int n, i;


    //延迟初始化,第一次调用putVal()时会初始化hashMap对象中的最耗费内存的散列表
    if ((tab = table) == null || (n = tab.length) == 0)
        n = (tab = resize()).length;
    //对应的写法:HashMap h  ;HashMap h = new HashMap();(只创建了没有放东西)
    //如果tab不为空的话,n就一定会被赋值。反过来说:n不被赋值只有一种情况:tab为null;

    //最简单的一种情况:寻址找到的桶位,正好是null。这个时候直接添加就好。
    if ((p = tab[i = (n - 1) & hash]) == null)
        tab[i] = newNode(hash, key, value, null);


    //不是最简单的情况。就说明,已经存在一个或多个元素(链表or红黑树)
    else {
        HashMap.Node<K,V> e; K k;
        //e:Node临时元素

        //寻址找到的元素p的哈希值和要添加的hash值相同  并且  (p的key与要添加的key相等,都为null   或者
        //(key不为空并且key和元素p的key相同)
        //如果条件满足,则e就会引用寻址找到的元素p。表示后续要进行替换操作。
        if (p.hash == hash &&
                ((k = p.key) == key || (key != null && key.equals(k))))
            e = p;

        //如果寻址找到的元素p已经树化了
        else if (p instanceof TreeNode)
            e = ((HashMap.TreeNode<K,V>)p).putTreeVal(this, tab, hash, key, value);

        //如果寻址找到的元素p已经成为链表
        else {
            //链表的情况,而且俩表的头元素和我们要输入的key不一致。
            for (int binCount = 0; ; ++binCount) {
                //迭代到最后一个元素了,也没有找到一个和要输入的key相同的元素
                //说明需要加入到当前链表的末尾。
                if ((e = p.next) == null) {
                    p.next = newNode(hash, key, value, null);
                    //当前链表的长度达到了树化阈值,需要进行树化
                    if (binCount >= TREEIFY_THRESHOLD - 1) // -1 for 1st
                        treeifyBin(tab, hash);
                    break;
                }
                //条件成立的话,说明找到了相同key的元素,需要进行替换操作。
                if (e.hash == hash &&
                        ((k = e.key) == key || (key != null && key.equals(k))))
                    break;
                p = e;
            }
        }

        //寻址找到的元素p的key为null或者与要添加的key相同,要替换value
        if (e != null) { // existing mapping for key
            V oldValue = e.value;
            if (!onlyIfAbsent || oldValue == null)
                e.value = value;
            afterNodeAccess(e);
            return oldValue;
        }
    }


    //modCount:表示散列表被修改的次数,替换Node元素的value的不计数
    ++modCount;
    //size:散列表中Node的数量。
    if (++size > threshold)
        resize();
    afterNodeInsertion(evict);
    return null;
}

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值