数据结构之HashMap源码详解(三)put方法源码分析

第一篇文章传送门
第二篇文章传送门
本篇开始put方法的分析,先看put的代码:

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

其实put方法调用了putVal方法,所以,put方法的核心还是putVal方法,在看putVal方法之前,putVal中有个hash方法,先来看下hash方法。

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

在之前提到过扰动函数,这个hash就是扰动函数。
扰动函数作用:让key的hash值的高16位也参与路由运算,增加散列性。
putVal分析

final V putVal(int hash, K key, V value, boolean onlyIfAbsent, boolean evict) {
    
        HashMap.Node[] tab;
        
        int n;
  
        if ((tab = this.table) == null || (n = tab.length) == 0) {
            n = (tab = this.resize()).length;
        }
      
        Object p;
        
        int i;
        
        if ((p = tab[i = n - 1 & hash]) == null) {
            tab[i] = this.newNode(hash, key, value, (HashMap.Node)null);
        } else {
           
            Object e;
            Object k;
           
            if (((HashMap.Node)p).hash == hash && ((k = ((HashMap.Node)p).key) == key || key != null && key.equals(k))) {
                e = p;
            } else if (p instanceof HashMap.TreeNode) {
                e = ((HashMap.TreeNode)p).putTreeVal(this, tab, hash, key, value);
            } else {
                int binCount = 0;

                while(true) {
          
                    if ((e = ((HashMap.Node)p).next) == null) {
                        ((HashMap.Node)p).next = this.newNode(hash, key, value, (HashMap.Node)null);
                        if (binCount >= 7) {//树化
                            this.treeifyBin(tab, hash);
                        }
                        break;
                    }
                    
                    if (((HashMap.Node)e).hash == hash && ((k = ((HashMap.Node)e).key) == key || key != null && key.equals(k))) {
                        break;
                    }

                    p = e;
                    ++binCount;
                }
            }
         

            if (e != null) {
                V oldValue = ((HashMap.Node)e).value;
                if (!onlyIfAbsent || oldValue == null) {
                    ((HashMap.Node)e).value = value;
                }

                this.afterNodeAccess((HashMap.Node)e);
                return oldValue;
            }
        }
        
        ++this.modCount;
       
        
        if (++this.size > this.threshold) {
            this.resize();
        }

        this.afterNodeInsertion(evict);
        return null;
    }

tab:引用当前hashMap的散列表;
n:表示散列表的长度;
p:当前散列表的元素;
i:表示路由寻址结果的下标;
e:一个临时元素,不为null的话,就找到了一个与当前要插入的key-val一致的key的元素;
k:临时的一个key;

if ((tab = this.table) == null || (n = tab.length) == 0) {
            n = (tab = this.resize()).length;
        }

创建散列表,将table赋值给tab,如果散列表为空,或者长度为0,那么就初始化散列表。
这样做可以防止HashMap创建完毕后却不使用而造成的浪费。这就是延迟初始化逻辑。
第一次调用putVal方法时会初始化hashMap对象中的最耗内存的散列表。

if ((p = tab[i = n - 1 & hash]) == null) {
            tab[i] = this.newNode(hash, key, value, (HashMap.Node)null);
        }

n-1& hash就是路由算法。
这个if语句就是寻址找到的位置刚好是null,这个时候就将当前的k-v封装为node扔进去。

if (((HashMap.Node)p).hash == hash && ((k = ((HashMap.Node)p).key) == key || key != null && key.equals(k))) {
                e = p;
            } else if (p instanceof HashMap.TreeNode) {
                e = ((HashMap.TreeNode)p).putTreeVal(this, tab, hash, key, value);
            } 

表示位置中的元素,与当前插入元素的key完全一致,表示后续需要进行替换操作

int binCount = 0;

                while(true) {
                    
                    if ((e = ((HashMap.Node)p).next) == null) {
                        ((HashMap.Node)p).next = this.newNode(hash, key, value, (HashMap.Node)null);
                        if (binCount >= 7) {//树化
                            this.treeifyBin(tab, hash);
                        }
                        break;
                    }
                     //条件成立的话,说明找到了相同的key的node元素,需要进行替换操作
                    if (((HashMap.Node)e).hash == hash && ((k = ((HashMap.Node)e).key) == key || key != null && key.equals(k))) {
                        break;
                    }

                    p = e;
                    ++binCount;
                }

p.next向下一位寻找,如果为空,那么就表示迭代到最后一个元素了,也没有找到一个与要插入key一致的node,说明需要加入到当前链表的末尾。

if (e != null) {
                V oldValue = ((HashMap.Node)e).value;
                if (!onlyIfAbsent || oldValue == null) {
                    ((HashMap.Node)e).value = value;
                }

                this.afterNodeAccess((HashMap.Node)e);
                return oldValue;
            }

当e不等于null,说明找打哦了一个与你插入元素key完全一致的数据,需要进行替换。

++this.modCount;
        
        
        if (++this.size > this.threshold) {
            this.resize();
        }

        this.afterNodeInsertion(evict);
        return null;

modCount表示散列表结构被修改次数,替换Node元素的value不计较。
size则是散列表中node的数量,threshold表示扩容阈值。
如果大于,那么就触发扩容。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值