HashMap之HashMap中hashSeed(hash种子)的作用分析

HashMap之HashMap中hashSeed(hash种子)的作用分析

1.扩容时需要调用transfer()进行节点的转移,此时传入第二个参数是是否需要rehash的boolean类型,即初始化hash种子

void resize(int newCapacity) {
    Entry[] oldTable = table;
    int oldCapacity = oldTable.length;
    if (oldCapacity == MAXIMUM_CAPACITY) {
        threshold = Integer.MAX_VALUE;
        return;
    }

    Entry[] newTable = new Entry[newCapacity];
    transfer(newTable, initHashSeedAsNeeded(newCapacity));
    table = newTable;
    threshold = (int)Math.min(newCapacity * loadFactor, MAXIMUM_CAPACITY + 1);
}

2.代码分析:

(1)首先hashSeed默认是0,currentAltHashing为false,sun.misc.VM.isBooted()判断jvm是否启动,开始运行为true

(2)只要(capacity >= Holder.ALTERNATIVE_HASHING_THRESHOLD)为true,useAltHashing就为true

(3)如果useAltHashing为true,因为currentAltHashing为false,且^(相同为0,不同为1),那么switching就为true

(4)如果switching为true,那么useAltHashing为true,那就可以给hashseed进行重新赋值,sun.misc.Hashing.randomHashSeed(this),

(5)所以根源在于第二步capacity >= Holder.ALTERNATIVE_HASHING_THRESHOLD是否为true,

ALTERNATIVE_HASHING_THRESHOLD = threshold;
threshold = (null != altThreshold)
        ? Integer.parseInt(altThreshold)
        : ALTERNATIVE_HASHING_THRESHOLD_DEFAULT;
String altThreshold = java.security.AccessController.doPrivileged(
    new sun.security.action.GetPropertyAction(
        "jdk.map.althashing.threshold"));
static final int ALTERNATIVE_HASHING_THRESHOLD_DEFAULT = Integer.MAX_VALUE;

(6)可以看到ALTERNATIVE_HASHING_THRESHOLD值为多少最终取决于altThreshold是否为null,而这个值我们又可以通过java虚拟机参数-D jdk.map.althashing.threshold=xxx进行配置,如果没有配置,默认为null,如果配置了,altThreshold就为我们配置的值,

(7)所以结论就是:如果我们配置了jdk.map.althashing.threshold值,Holder.ALTERNATIVE_HASHING_THRESHOLD就为我们配置的值,如果没有配置就为Integer.MAX_VALUE,

那么也就是说:因为数组容量不可能达到Integer.MAX_VALUE,如果没有配置capacity >= Holder.ALTERNATIVE_HASHING_THRESHOLD就为false,switching也为false,也就不需要rehash,如果配置了,一旦capacity达到了我们配置的值,switching为true,就需要重新计算一个hashseed的值,并且rehash

final boolean initHashSeedAsNeeded(int capacity) {
    boolean currentAltHashing = hashSeed != 0;
    boolean useAltHashing = sun.misc.VM.isBooted() &&
            (capacity >= Holder.ALTERNATIVE_HASHING_THRESHOLD);
    boolean switching = currentAltHashing ^ useAltHashing;
    if (switching) {
        hashSeed = useAltHashing
            ? sun.misc.Hashing.randomHashSeed(this)
            : 0;
    }
    return switching;
}

3.如果rehash为true,也就是需要rehash,那么就需要重新计算hash值,调用hash()

void transfer(Entry[] newTable, boolean rehash) {
    int newCapacity = newTable.length;
    for (Entry<K,V> e : table) {
        while(null != e) {
            Entry<K,V> next = e.next;
            if (rehash) {
                e.hash = null == e.key ? 0 : hash(e.key);
            }
            int i = indexFor(e.hash, newCapacity);
            e.next = newTable[i];
            newTable[i] = e;
            e = next;
        }
    }
}

4.如果一旦调用了rehash()方法,那么hashseed一定也被重新赋值了,我们就需要跟据其他算法,再次计算一下hash()值

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

    h ^= k.hashCode();

    // This function ensures that hashCodes that differ only by
    // constant multiples at each bit position have a bounded
    // number of collisions (approximately 8 at default load factor).
    h ^= (h >>> 20) ^ (h >>> 12);
    return h ^ (h >>> 7) ^ (h >>> 4);
}
总结:使用hash种子的目的就是:用hashseed决定获取hash值的方式,如果一旦数组容量超过了我们自己配置的值,就会生成新的hash种子,不再通过异或方式获取hash值,而是通过调用sun.misc.Hashing.stringHash32()方法,获取hash值,从而提高hash值的散列性,减小hash碰撞的产生,缩短了链表的长度,提高了效率。

视频链接:https://www.bilibili.com/video/BV1x741117jq?p=3&spm_id_from=333.1007.top_right_bar_window_history.content.click

  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值