码迷,mamicode.com
首页 > 其他好文 > 详细

HashMap源码学习

时间:2015-06-15 16:35:27      阅读:116      评论:0      收藏:0      [点我收藏+]

标签:hashmap

基础变量

/**
     * The default initial capacity - MUST be a power of two.
     */
    static final int DEFAULT_INITIAL_CAPACITY = 16;
    /**
     * The maximum capacity, used if a higher value is implicitly specified
     * by either of the constructors with arguments.
     * MUST be a power of two <= 1<<30.
    */
   static final int MAXIMUM_CAPACITY = 1 << 30;

    /**
     * The load factor used when none specified in constructor.
     */
    static final float DEFAULT_LOAD_FACTOR = 0.75f;

    /**
     * The table, resized as necessary. Length MUST Always be a power of two.
     */
    transient Entry[] table;

HashMap默认的初始容量为16,同时还强调了容量必须是2的幂次方
最大容量为1<<30。默认的装载因子 为0.75,这个下面再说。
其主体是一个Entry数组table,当需要时会重新分配大小,但是长度依然必须是2的幂次方

装载因子

装载因子=实际放入的元素/HashMap的总容量。
为了避免元素冲突,容量都会比放入的元素大,因此越小的装载因子意味着元素发生碰撞的概率越低,但是也意味着浪费空间的概率会越高。

容量:2的幂次方

HashMap的容量从来都是2的幂次方,虽然插入的数量/装载因子 得到的数字可能不是2的幂次方,但是该容量在申请的时候从来都会变成2的幂次方,这是有原因的。

搜寻位置:哈希与再哈希


    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;
    }

首先判断key是否为空,hashMap是支持放入null作为key的。
其次,对key的hashCode进行再哈希得到hash。

为何需要进行再哈希呢?事关如下这个函数:indexFor。

    /**
     * Returns index for hash code h.
     */
    static int indexFor(int h, int length) {
        return h & (length-1);
    }

这个函数没有什么特殊,仅仅是使用了位运算来代替求余运算。得到的余数对应着元素的位置。
但是这有个问题:由于length是2的幂次方,length-1就成为了除了最高位为0,其余位全都为1的值,此时如果单纯的通过key的hash进行计算,如果hash的对应低位没变(这很有可能,比如length-1=0111,而hash为1111,10111,11111,110111等等),由于取余结果仅仅跟这些低位有关,而这些低位的值从来没有为了区分整个key.hashCode而做过优化,他们无法代表整个key.hashCode做出区分,因此会导致冲突的高发率
因此,put方法中在indexFor之前,对原有的key.hashCode进行再次hash,使得再哈希结果中的1尽量均匀而且随机的分布在所有数位上,这样求余之后得到的结果碰撞率就减小了。
再哈希方法如下:

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

这就是hashMap容量为何要做到2的幂次方,同时,hashMap的put方法为何要再次hash的意义。
如果还是没有弄懂这么做的意义的话,请仔细琢磨加黑的语句。

HashMap源码学习

标签:hashmap

原文地址:http://blog.csdn.net/langduhualangdu/article/details/46503855

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!