HashMap源码解析——学习笔记

hashCode() 说起

hashCode 是jdk根据对象的地址或者字符串或者数字算出来的int类型的数值

hashCode 契约

这个契约在 hashCode 方法的 JavaDoc 中进行了阐述。它可以大致的归纳为下面几点:

1.在一个运行的进程中,相等的对象必须要有相同的哈希码

2.请注意这并不意味着以下常见的误解:

3.不相等的对象一定有着不同的哈希码——错!

4.有同一个哈希值的对象一定相等——错!

不同对象的数量经常比可能的哈希吗的数量 (2^32)更大;

什么是HashMap? 主题

HashMap 是用于储存(key-value)键值对集合,每一个键值对应Entry,这些键储存在一个数据中,数据是HashMap的主干。
内部是通过数组与链表实现的,可以理解为“链表的数组”。

put操作做了什么?

    public V put(K key, V value) {
        if (key == null)
            return putForNullKey(value);
        //计算获取hash值
        int hash = hash(key.hashCode());
        //获取此次hash值在数组(bucket)的下标位置
        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;
    }

    void addEntry(int hash, K key, V value, int bucketIndex) {
        Entry<K,V> e = table[bucketIndex];
        table[bucketIndex] = new Entry<>(hash, key, value, e);
        if (size++ >= threshold)
            resize(2 * table.length);
    }

1、可以看出put储存对象时候,先调用hashCode计算hash从而得到bucket位置。

2、如果索引碰撞,遍历链表,判断是否有key存在,若存在更新value值。(不过在JDK1.8中链表元素超过8个时,会把链表结构转换成红黑树结构存储,提高查询的性能)

3.若未碰撞,直接放入表中。

4.放入前,如果元素个数超过负载因子的比例,则进行rehash,扩容,之会插入。

5.null key的元素永远会放到index为0的链表里面。

注意:当新加是达到量的阀值( threshold = (int)(DEFAULT_INITIAL_CAPACITY * DEFAULT_LOAD_FACTOR) ) 默认容器大小16*负载因子0.75f 则开始扩容。容量大小是之前的2倍。

hash算法是什么?

    JDK1.7
    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);
    }
    This function ensures that hashCodes that differ only by

    JDK1.8
    static final int hash(Object key) {
        int h;
        return (key == null) ? 0 : (h = key.hashCode()) ^ (h >>> 16);
    }

当数组长度为2的n次幂的时候,不同的key算得得index相同的几率较小,那么数据在数组上分布就比较均匀,也就是说碰撞的几率小,相对的,查询的时候就不用遍历某个位置上的链表。

知识点:
当length总是2的n次方时, h & (length - 1) 等价于 hash对length取模 ,但是&比%具有更高的效率;
在n - 1为15(0x1111)时,其实散列真正生效的只是低4bit的有效位,容易碰撞;

JDK1.8
通过hashCode()的高16位异或低16位实现的:(h = k.hashCode()) ^ (h >>> 16),主要是从速度、功效、质量来考虑的,这么做可以在数组table的length比较小的时候,也能保证考虑到高低Bit都参与到Hash的计算中,同时不会有太大的开销。

modCount干什么用?

HashMap中有一个成员变量modCount,这个用来实现“fast-fail”机制(也就是快速失败)。所谓快速失败就是在并发集合中,其进行迭代操作时,若有其他线程对其结构性的修改,这是迭代器会立马感知到,当 expectedModCount!= modCount,立刻抛出ConcurrentModificationException异常,不需要等待迭代完成之后才告诉你已经出错。

get 如何实现

    public V get(Object key) {
        if (key == null)
            return getForNullKey();
        int hash = hash(key.hashCode());
        for (Entry<K,V> e = table[indexFor(hash, table.length)];
             e != null;
             e = e.next) {
            Object k;
            if (e.hash == hash && ((k = e.key) == key || key.equals(k)))
                return e.value;
        }
        return null;
    }

    private V getForNullKey() {
        for (Entry<K,V> e = table[0]; e != null; e = e.next) {
            if (e.key == null)
                return e.value;
        }
        return null;
    }

1、判断key值是否为null,在table[0]的链表中寻找;

2、计算hash值,进而获得table中的index值

3、在table[index]的链表中寻找,根据hash值和equals()方法获得相应的value。

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值