Java HashMap 分析之三放入元素

http://blog.csdn.net/csfreebird/article/details/7355282

现在,有了hash code,来考虑如何计算放入数组的位置。hash code值通常会很大,但是数组的大小有限,默认只有16,大的也不能超过230次方。所以,用模运算来保证在数组大小范围内是合理的,比如:index = hash code % array size.不过这有点慢,JDK采用了更快的算法。这个更快的算法源于一个数学规律,就是如果size2N次方,那么数Xsize的模运算结果等价于Xsize-1的按位与运算,也就是 X % size <=> X & (size -1).按位与只消耗一个CPU周期,当然快多了。现在就可理解为什么要故意把数组大小弄成2N次方了。再回头看一开始计算数组大小的代码,完全理解了。

[java] view plaincopyprint?

1. int capacity = 1;  

2. while (capacity < initialCapacity)  

3.       capacity <<= 1;  


比如size=16,二进制表示如下:(32位)
0000000000000000000000000010000
size-1=15,表示如下:
0000000000000000000000000001111

假如hash code=4
0000000000000000000000000000100
4 & 15 结果为:
0000000000000000000000000000100

假如hash code=6
0000000000000000000000000000101
6 & 15 结果为:
0000000000000000000000000000101

假如hash code=38
0000000000000000000000000100110
38 & 15 结果为:
0000000000000000000000000000110

通过观察这三个例子,又可以发现一个特点,也就是X & size-1 的结果受到了size的阶数的限制,这里size=16,阶数为4.结果就是只用低4位的1X按位与,而X的高位没有用到。这会导致重复率相当高。如果用一个算法将X的低位重新计算,比如根据所有位的值进行重新计算,就可以使得hash值分布更均匀。下面的代码揭示了在真正按位与之前,调用了hash函数,进行了一堆位运算。至于为什么用这个算法,我也不知道其来历。不过这里一篇帖子演示了这个hash函数会导致bit的随机性,可以作为理解的开始。

http://stackoverflow.com/questions/9335169/understanding-strange-java-hash-function

public V put(K key, V value) {

        if (key == null)

            return putForNullKey(value);

        int hash = hash(key.hashCode());

        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;

    }

 

    static int hash(int h) {

        // 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);

    }

 

    static int indexFor(int h, int length) {

        return h & (length-1);//真正按位与

    }

 

    void addEntry(int hash, K key, V value, int bucketIndex) {

        Entry<K,V> e = table[bucketIndex];

        table[bucketIndex] = new Entry<K,V>(hash, key, value, e);

        if (size++ >= threshold)

            resize(2 * table.length);

    }

上面的for循环是查找并替换符合条件的对象,如果找不到,则添加新的对象。查找到的条件(必须都满足)是:
1.hash值相等
2.key的引用相同或者key的值相等。

 

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值