【八股学习】HashMap源码总结

初始化

构造方法

可见,HashMap有四种构造方法:

其中1、3、4可以归为一类:使用默认的或者指定的初始化容量和负载因子,如果使用默认容量16,则会在第一次插入时在 resize 中自行计算 threshold。如果自行指定参数则直接赋值(通过 tableSizeFor 方法扩容到与 initialCapacity 最接近的 2 的幂次方大小) threshold,然后进行扩容判断。

    // 默认构造函数。
    public HashMap() {
   
   
        this.loadFactor = DEFAULT_LOAD_FACTOR; // all   other fields defaulted
     }

     // 包含另一个“Map”的构造函数
     public HashMap(Map<? extends K, ? extends V> m) {
   
   
         this.loadFactor = DEFAULT_LOAD_FACTOR;
         putMapEntries(m, false);//下面会分析到这个方法
     }

     // 指定“容量大小”的构造函数
     public HashMap(int initialCapacity) {
   
   
         this(initialCapacity, DEFAULT_LOAD_FACTOR);
     }

     // 指定“容量大小”和“负载因子”的构造函数
     public HashMap(int initialCapacity, float loadFactor) {
   
   
         if (initialCapacity < 0)
             throw new IllegalArgumentException("Illegal initial capacity: " + initialCapacity);
         if (initialCapacity > MAXIMUM_CAPACITY)
             initialCapacity = MAXIMUM_CAPACITY;
         if (loadFactor <= 0 || Float.isNaN(loadFactor))
             throw new IllegalArgumentException("Illegal load factor: " + loadFactor);
         this.loadFactor = loadFactor;
         // 初始容量暂时存放到 threshold ,在resize中再赋值给 newCap 进行table初始化
         this.threshold = tableSizeFor(initialCapacity);
     }

其中第二种使用其他Map进行初始化的方法如下:
1、如果还没有初始化,则先通过负载因子和传入map的元素个数计算所需的最小容量。然后进行最大上界判断。如果最小容量大于threshold,那就初始化为 tableSizeFor 的结果。这里的 if (t > threshold) 实际上永远都为真,因为未初始化时 threshold = 0,使用这个同之前一样是为了之后扩容 resize 做准备

2、初始化了则进行判断并resize

3、最后放入到新的map中,如果是未初始化的table会在putVal中resize初始化扩容。

final void putMapEntries(Map<? extends K, ? extends V> m, boolean evict) {
   
   
    int s = m.size();
    if (s > 0) {
   
   
        // 判断table是否已经初始化
        if (table == null) {
   
    // pre-size
            /*
             * 未初始化,s为m的实际元素个数,ft=s/loadFactor => s=ft*loadFactor, 跟我们前面提到的
             * 阈值=容量*负载因子 是不是很像,是的,ft指的是要添加s个元素所需的最小的容量
             */
            float ft = ((float)s / loadFactor) + 1.0F;
            int t = ((ft < (float)MAXIMUM_CAPACITY) ?
                    (int)ft : MAXIMUM_CAPACITY);
            /*
             * 根据构造函数可知,table未初始化,threshold实际上是存放的初始化容量,如果添加s个元素所
             * 需的最小容量大于初始化容量,则将最小容量扩容为最接近的2的幂次方大小作为初始化。
             * 注意这里不是初始化阈值
             */
            if (t > threshold)
                threshold = tableSizeFor(t);
        }
        // 已初始化,并且m元素个数大于阈值,进行扩容处理
        else if (s > threshold)
            resize();
        // 将m中的所有元素添加至HashMap中,如果table未初始化,putVal中会调用resize初始化或扩容
        for (Map.Entry<? extends K, ? extends V> e : m.entrySet()) {
   
   
            K key = e.getKey();
            V value = e.getValue();
            putVal(hash(key), key, value, false, evict);
        }
    }
}

put方法

jdk1.8

HashMap 只提供了 put 用于添加元素,putVal 方法只是给 put 方法调用的一个方法,并没有提供给用户使用。

  1. table未初始化或者长度为0,进行扩容
  2. (n - 1) & hash 确定元素存放在哪个桶中,如果桶为空,则直接生成新结点放入桶中
  3. 桶中存在元素,则发生了hash冲突,判断第一个结点的key和插入key是否相同,如果相同则用新的value值替换掉旧的value值。
  4. 如果第一个结点是红黑树结点,则调用方法插入到树中
  5. 不是树节点则开始遍历链表找是否存在相同的key来替换或插入
  6. 如果找到key相同的结点则停止遍历,并替换value值
  7. 没有找到,则在链表尾部插入新结点
  8. 更改modCount和size并判断是否需要进行扩容
public V put(K key, V value) {
   
   
    return putVal(hash(key), key, value, 
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值