HashMap集合put函数源码阅读记录

在阅读put函数源码前,首先看看会用到的几个变量

/**
 * 用于记录HashMap集合中增、删、改的次数
 * 该值会在初始化迭代器时存储在一个局部变量expectedModCount中
 * 由于HashMap不是线程安全的,因此在使用迭代器时,如果有其它线程修改了map,会造成modCount的值与expectedModCount不一至,将会抛出ConcurrentModificationException异常
 */
transient int modCount;

/**
 * 用于表示key为空的条目
 */
transient HashMapEntry<K, V> entryForNullKey;

/**
 * HashMap集合大小的阈值
 */
private transient int threshold;

首先看Map集合中最常用的 put(K key, V value) 函数

/**
 * Maps the specified key to the specified value.
 *
 * @param key
 *            the key.
 * @param value
 *            the value.
 * @return the value of any previous mapping with the specified key or
 *         {@code null} if there was no such mapping.
 */
@Override public V put(K key, V value) {
    if (key == null) {
        return putValueForNullKey(value);
    }
    // 计算key的hash值
    int hash = Collections.secondaryHash(key);
    HashMapEntry<K, V>[] tab = table;
    // 根据hash值计算存储的位置
    int index = hash & (tab.length - 1);
    
    // 循环偏历entry数组,判断key对应的entry是否存在,如果对应的entry已经存在,则直接将旧的entry.value修改为新的value,并且返回旧的value
    for (HashMapEntry<K, V> e = tab[index]; e != null; e = e.next) {
        // 判断hash、key值是否完全都相同
        if (e.hash == hash && key.equals(e.key)) {
            preModify(e);
            V oldValue = e.value;
            e.value = value;
            return oldValue;
        }
    }

    // No entry for (non-null) key is present; create one
    modCount++; // 记录修改次数
    // 判断当前map集合的长度是否超出阈值
    if (size++ > threshold) {
        // 扩容,并且重新生成Hash表
        tab = doubleCapacity();
        // 根据新生成的hash表重新计算存储的位置
        index = hash & (tab.length - 1);
    }
    // 创建对应的entry数据并保存在对应的存储位置
    addNewEntry(key, value, hash, index);
    return null;
}

当存储的key值为null时会调用putValueForNullKey(V value),当key值不为空的情况见注释,putValueForNullKey函数代码如下

private V putValueForNullKey(V value) {
    HashMapEntry<K, V> entry = entryForNullKey;
    if (entry == null) {
        addNewEntryForNullKey(value);
        size++;
        modCount++;
        return null;
    } else {
        preModify(entry);
        V oldValue = entry.value;
        entry.value = value;
        return oldValue;
    }
}

在这里判断了,当entry为空时,会先调用addNewEntryForNullKey()函数,调用该函数创建一个HashMapEntry对象,新创建出来的HashMapEntry被entryForNullKey所引用。
当entry不为空时,说明之前已经存在一个entryForNullKey的引用,则直接将旧的value替换成新的value,并且返回旧的value值。

/**
 * Creates a new entry for the null key, and the given value and
 * inserts it into the hash table. This method is called by put
 * (and indirectly, putAll), and overridden by LinkedHashMap.
 */
void addNewEntryForNullKey(V value) {
    entryForNullKey = new HashMapEntry<K, V>(null, value, 0, null);
}

猜你喜欢

转载自my.oschina.net/u/3269106/blog/1627176
今日推荐