HashMap的hashSeed的问题

HashMap的详细介绍

—–本文只针对1.7版本的HashMap所讲解.

我们知道了HashMap的生成hash码的时候会设计到hashSeed的问题,

    public V put(K key, V value) {

        if (table == EMPTY_TABLE) {
            inflateTable(threshold);
        }
        if (key == null)
            return putForNullKey(value);
            //对key生成hash码
        int hash = hash(key);
        int i = indexFor(hash, table.length);
        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;
            }
        }

        modCount++;
        addEntry(hash, key, value, i);
        return null;
    }

这里对key进行生成hash码,它是怎么生成的呢?我们看下面的代码:


transient int hashSeed = 0;

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

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

我们这里一步一步来解释,首先hashSeed怎么来的?它是HashMap的属性,在HashMap初始化时已经为0,但是我们在执行inflateTable方法时执行了initHashSeedAsNeeded方法这里面对hashSeed又执行了一次赋值操作,我们来代码:

    private void inflateTable(int toSize) {
        // Find a power of 2 >= toSize
        int capacity = roundUpToPowerOf2(toSize);

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

    //hashSeed在这个方法里面又进行了一次赋值操作,为了让读者了解的更多,我尽量做的详细一点.
    final boolean initHashSeedAsNeeded(int capacity) {
    //当我们初始化的时候hashSeed为0,0!=0 这时为false.
        boolean currentAltHashing = hashSeed != 0;
        //isBooted()这个方法里面返回了一个boolean值,我们看下面的代码
        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;
    }

sun.misc.VM类的代码如下:

    private static volatile boolean booted = false;
    public static boolean isBooted() {
        return booted;
    }

这里返回的是booted 的值,但是booted 默认为false,但是我们不知道VM启动的时候是否对它又赋了新值,怎么办呢?我们可以用一个土办法来测试如下:这里写图片描述
我们可以看到在Map执行前booted的值为true,然而我们HashMap执行的时候并没有给它赋值,所以它为true,然后我们比较后面的条件看看,我们看见要拿初始容量比较Holder.ALTERNATIVE_HASHING_THRESHOLD,但是我们不知道这个为多少.看代码.


    static final int ALTERNATIVE_HASHING_THRESHOLD_DEFAULT = Integer.MAX_VALUE;//这里为2147483647,它是HashMap的属性,初始化的时候就已赋值
    //Holder这个类是HashMap的子类,
     private static class Holder {
     //这里定义了我们需要的常量,但是它没赋值,我们看看它是怎么赋值的?
        static final int ALTERNATIVE_HASHING_THRESHOLD;

        static {
            String altThreshold = java.security.AccessController.doPrivileged(
                new sun.security.action.GetPropertyAction(
                    "jdk.map.althashing.threshold"));

            int threshold;
            try {
                threshold = (null != altThreshold)
                        ? Integer.parseInt(altThreshold)
                        : ALTERNATIVE_HASHING_THRESHOLD_DEFAULT;

                // disable alternative hashing if -1
                if (threshold == -1) {
                    threshold = Integer.MAX_VALUE;
                }

                if (threshold < 0) {
                    throw new IllegalArgumentException("value must be positive integer.");
                }
            } catch(IllegalArgumentException failed) {
                throw new Error("Illegal value for 'jdk.map.althashing.threshold'", failed);
            }

            ALTERNATIVE_HASHING_THRESHOLD = threshold;
        }
    }

我们通过代码看到ALTERNATIVE_HASHING_THRESHOLD来自threshold,threshold哪里呢?看上面得知来自判断条件里面.那我们就来看看判断条件altThreshold,altThreshold来自一个本地方法,我们还是用老方法,看看它的值为什么.

    @CallerSensitive
    public static native <T> T doPrivileged(PrivilegedAction<T> action);

这里写图片描述
这里为null,我们就可以看上面的代码,最后可以看见我们的ALTERNATIVE_HASHING_THRESHOLD =2147483647,我们在返上面比较16>=2147483647为false,true && false返回false.

final boolean initHashSeedAsNeeded(int capacity) {
      //通过上面的过程,我们知道了currentAltHashing =false
        boolean currentAltHashing = hashSeed != 0;
       //useAltHashing = false
        boolean useAltHashing = sun.misc.VM.isBooted() &&
                (capacity >= Holder.ALTERNATIVE_HASHING_THRESHOLD);
     // false ^ false 结果为false,switching为false
        boolean switching = currentAltHashing ^ useAltHashing;
        if (switching) {
            hashSeed = useAltHashing
                ? sun.misc.Hashing.randomHashSeed(this)
                : 0;
        }
        //返回false
        return switching;
    }

通过上面的流程以后,我们知道hashSeed 并没有重新赋值,最终hashSeed的值为0;


书山有路勤为径,学海无涯苦作舟。

  • 5
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 4
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 4
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值