HashMap工作原理

简介

HashMap是根据键的hashCode值来存储数据,HashMap允许key、value为NULL,非线程安全。

数据结构

HashMap是数组和链表的结合体,数组中每一个元素是Entry,Entry是包含键值对的链表,Entry源码如下:

static class Entry<K,V> implements Map.Entry<K,V> {
    final K key;
    V value;
    Entry<K,V> next;
    final int hash;
    ……
}

在这里插入图片描述

存储

根据key的hashCode找到bucket位置来存储Entry对象,如果hashCode值相同发生“碰撞”,则将Entry对象存储在链表当中,源码如下:

public V put(K key, V value) {
    // HashMap允许存放null键和null值。
    // 当key为null时,调用putForNullKey方法,将value放置在数组第一个位置。
    if (key == null)
        return putForNullKey(value);
    // 根据key的keyCode重新计算hash值。
    int hash = hash(key.hashCode());
    // 搜索指定hash值在对应table中的索引。
    int i = indexFor(hash, table.length);
    // 如果 i 索引处的 Entry 不为 null,通过循环不断遍历 e 元素的下一个元素。
    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;
        }
    }
    // 如果i索引处的Entry为null,表明此处还没有Entry。
    modCount++;
    // 将key、value添加到i索引处。
    addEntry(hash, key, value, i);
    return null;
}

读取

根据key的hashCode找到bucket位置,然后循环遍历Entry链表,通过key的equals()方法找到对应的value,源码如下:

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

扩容

假设数组大小为n(默认值16),当元素个数大于n * loadFactor(默认值0.75),数组大小将扩容一倍,并将原来的对象放入新的bucket数组中,这个过程叫作rehashing,因为它将调用hash方法找到新的bucket位置,在调整大小的过程中,存储在链表中的元素次序会反过来,因为移动到新的bucket位置的时候,HashMap并不会将元素放在链表的尾部,而是放在头部,这是为了避免尾部遍历(tail traversing)。

在多线程环境下,可能产生条件竞争(race condition),因为如果两个线程都发现HashMap需要重新调整大小,它们会同时操作,所以是非线程安全。

总结

HashMap通过key的hashCode找到bucket位置,并将数据存放在Entry链表当中,或者通过key的equals方法循环遍历链表获取对应的value。实际使用时,应该提前预估HashMap的容量,并据此设置相应的数组大小,尽量避免扩容,因为它是一个非常耗时的过程。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值