计算key的hash值,通过hash值决定Entry存放到数组的哪个索引位置

34 阅读2分钟
final V putVal(K key, V value, boolean onlyIfAbsent) {
    // key和value如果为null,直接甩异常
    if (key == null || value == null) throw new NullPointerException();
    // 计算key的hash值,(通过hash值决定Entry存放到数组的哪个索引位置)
    int hash = spread(key.hashCode());
    // 暂时当做标识,值为0
    int binCount = 0;
    // 声明临时变量为tab,tab赋值了table,table就是当前HashMap的数组!这是个死循环
    for (Node<K,V>[] tab = table;;) {
    	// 声明了一堆变量
        //f-当前索引位置的数据
        //n-数组长度
        //i-数据要存储的索引位置
        //fh-桶位置数据的hash值
        Node<K,V> f; int n, i, fh;
    	// 如果tab为null,或者tab的长度为0
        if (tab == null || (n = tab.length) == 0)
        	// 进来说明数组没有初始化,开始初始化,ConcurrentHashMap要避免并发初始化时造成的问题
            tab = initTable();
        
        // tabAt(数组,索引位置),得到这个数组指定索引位置的值,f就是数组的下标位置的值 
        // 如果f == null
        else if ((f = tabAt(tab, i = (n - 1) & hash)) == null) {
        	// 进到这,说明索引位置没有值,基于CAS的方式将当前的key-value存储到这个索引位置
            if (casTabAt(tab, i, null,
                         new Node<K,V>(hash, key, value, null)))
            	// 如果CAS成功,添加数据成功(添加到了数组上),如果走false,继续上述操作,尝试其他内容
                break;                   // no lock when adding to empty bin
        }
    	// f是经过上述if得到的索引位置的值,当前key-value的hash是否为MOVED,如果相等,证明当前位置正在扩容
        else if ((fh = f.hash) == MOVED)//MOVED表示正在扩容
        // 如果正在扩容,帮你扩容(构建长度为原来2倍的数组,并且将老数组的值移动到新数组),帮助扩容的操作是迁移数据的操作
            tab = helpTransfer(tab, f);
        else {
            // 第一个判断:数组初始化了么? 
            // 第二个判断:数组指定的位置有值么? 
            // 第三个判断:现在正在扩容么? 
            // 这个else就是第四个判断:是否需要将数据挂到链表上,或者添加到红黑树中?(出现了Hash冲突(碰撞))
            V oldVal = null;
            // 加个锁,锁的是f(f是数组的下标位置的值),也就是在这,锁住了这个桶
            synchronized (f) {
                // 拿到i索引位置的数据,判断与锁住的f是不是同一个
                if (tabAt(tab, i) == f) {
                    // 如果fh 大于等于 0(判断hash值是否大于等于0),说明是链表
                    if (fh >= 0) {
                    
                    https://www.laipuhuo.com/goodsDetail/02034e4fd214457cb39525d5ae8d4251.html
https://www.laipuhuo.com/goodsDetail/02054AB7DF9949A0A14B033C691FD46D.html
https://www.laipuhuo.com/goodsDetail/0208ca5e75df4f958268e65143dc0839.html
https://www.laipuhuo.com/goodsDetail/0208e852831f415d8e45eb499f53bc56.html
https://www.laipuhuo.com/goodsDetail/020C6EFAFCDE478186CE2DA49315B0FC.html
https://www.laipuhuo.com/goodsDetail/020D3D18234B4A868AA3C38EFE322C0C.html
https://www.laipuhuo.com/goodsDetail/020d975c69074ccc99430a8d348cf16d.html
                        // 将标识修改为1
                        binCount = 1;
                        // 开始循环,为了将插入的值挂到链表的最后面
                        for (Node<K,V> e = f;; ++binCount) {
                            K ek;
                            if (e.hash == hash && // 判断指向的节点的key是否等于当前要插入的节点的key
                                ((ek = e.key) == key ||  // 判断指向的节点的key是否等于当前要插入的节点的key
                                 (ek != null && key.equals(ek)))) { // 指向的节点的key是否域当前的key相等
                                 // 说明当前是修改操作
                                oldVal = e.val;
                                if (!onlyIfAbsent) // onlyIfAbsent如果为true,就什么事都不做
                                    e.val = value; // 修改值
                                break;