JDK1.7的HashMap源码

1、构造函数(默认)

static final int DEFAULT_INITIAL_CAPACITY = 16;
static final float DEFAULT_LOAD_FACTOR = 0.75f;
public HashMap() {
    this(DEFAULT_INITIAL_CAPACITY, DEFAULT_LOAD_FACTOR);
}

默认的大小为16,负载因子为0.75.

2、哈希算法

final int hash(Object k) {
    int h = 0;
    if (useAltHashing) {
        if (k instanceof String) {
            return sun.misc.Hashing.stringHash32((String) k);
        }
        h = hashSeed;
    }

    h ^= k.hashCode();

    h ^= (h >>> 20) ^ (h >>> 12);
    return h ^ (h >>> 7) ^ (h >>> 4);
}  

9次扰动处理=4次位运算+5次异或。
此算法加入了高位计算,防止低位不变,高位变化时,造成的 hash 冲突。
hashCode值相同,这里的哈希结果值也相同。

3、put方法

public V put(K key, V value) {
	//HashMap 允许存放 null 键和 null 值。
    if (key == null)
        return putForNullKey(value);
    int hash = hash(key);
    //h & (length-1) 运算确定对应 table 中的索引
    int i = indexFor(hash, table.length);
    //如果 i 处的 Entry 不为 null,通过循环不断遍历 e 元素的下一个元素。
    for (Entry<K,V> e = table[i]; e != null; e = e.next) {
        Object k;
        if (e.hash == hash && ((k = e.key) == key || key.equals(k))) {
            V oldValue = e.value;
            e.value = value;
            e.recordAccess(this);
            return oldValue;
        }
    }
	// 到了这里,说明上面循环遍历都没有进入if条件
    modCount++;
    // 将 key、 value 添加到 i 索引处
    addEntry(hash, key, value, i);
    return null;
}    

4、indexFor方法

static int indexFor(int h, int length) {
    return h & (length-1);
}    

当 length 总是 2 的 n 次方时, h& (length-1)运算等价于对 length 取模。

5、addEntry方法

void addEntry(int hash, K key, V value, int bucketIndex) {
	//扩容条件判断
   if ((size >= threshold) && (null != table[bucketIndex])) {
        //把 table 对象的长度扩充到原来的 2 倍。
        resize(2 * table.length);
        hash = (null != key) ? hash(key) : 0;
        bucketIndex = indexFor(hash, table.length);
    }
	//添加节点
    createEntry(hash, key, value, bucketIndex);
}    

这个方法用于添加节点,也负责扩容。

6、createEntry方法

void createEntry(int hash, K key, V value, int bucketIndex) {
	//获取指定索引处的Entry
    Entry<K,V> e = table[bucketIndex];
    //将新创建的 Entry 放入 bucketIndex 索引处,并让新的 Entry 指向原来的 Entry(头插法)
    table[bucketIndex] = new Entry<>(hash, key, value, e);
    size++;
}

新创建一个节点,并让新节点的next指针指向e

new Entry<>(hash, key, value, e);

7、putForNullKey方法

private V putForNullKey(V value) {
    for (Entry<K,V> e = table[0]; e != null; e = e.next) {
        if (e.key == null) {
            V oldValue = e.value;
            e.value = value;
            e.recordAccess(this);
            return oldValue;
        }
    }
    modCount++;
    addEntry(0, null, value, 0);
    return null;
}

当put一个 key 为 null 时,调用 该方法,将 value 放置在数组第一个位置。

发布了104 篇原创文章 · 获赞 5 · 访问量 1万+

猜你喜欢

转载自blog.csdn.net/zjuwzp/article/details/103518661
今日推荐