Java HashMap 实现源代码分析

本文深入探讨了HashMap的工作原理,包括其内部结构、哈希算法、冲突解决策略及扩容机制。通过详细解析源代码,帮助读者理解如何高效使用HashMap。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

最近在用HashMap的时候,碰到一些问题,有疑惑,遂查看源代码分析了一下。

我们知道,HashMap<K, V>是以键值对来读写数据的,那它底层用于存放Value的什么?又如何判断两个Key是否相等呢?

首先说说哈希表的一些相关概念,可能有些名词会有不同的叫法。

  • 容量:散列表中散列数组大小
  • 散列运算:key->散列值(散列数组下标)的算法, h ^= (h >>> 20) ^ (h >>> 12);return h ^ (h >>> 7) ^ (h >>> 4);
  • 散列桶:散列值相同的元素的“线性集合”
  • 加载因子(loadFactor):散列数组的加载率,一般小于75%比较理想(即元素数量/散列数组大小)。
    • threshold = (int)(newCapacity * loadFactor);//阈值=新的容量*加载因子,当新增元素时达到了阈值就要再次扩容
  • 散列查找:根据Key计算散列值,根据散列值(下标)找到在散列桶,在散列桶中顺序比较Key,如果相同(Key的hascode相同、且equals()为true)就返回Value;散列表中Value可以重复,只要其对应的Key不一样即可;如果Key相同,新添加的元素会覆盖原来的Value。
再来看看HashMap类中的几个主要成员变量:
  • Entry[] table;//Entry<K, V>型的数组,用于存放Value,每个Entry元素里有Key, Value, hash与next(指向下一个Entry)
  • int size;//所有Value的数量,Map的实际容量
  • int threshold;//阀值,超过之后要对table扩容
  • final float loadFactor;//加载因子
table实际上是一个Entry<K, V>型的数组,它就是用于存放键值对的,而不同的Key可能会算出相同的hash code,因此,table的某一个下标可能会有多个Entry, 而Entry里有一个Entry<K, V> next的属性,用于指向下一个Entry——实际上就是一个链表,这样,key相同的Value也能放到table的同一个置,通过next来引用下一个Entry.
Entry[] table
EntryEntryEntryEntryEntry
   
e.next
   
e.next
   
 null
同一个散列桶中有多个Entry时

Entry:HashMap中的内部类
static class Entry<K,V> implements Map.Entry<K,V> {
        final K key;
        V value;

     /*Entry 这里是一个链表,也就是散列桶中的线性集合:
     *  当hash code相同时,位于table的同一个下标中,能过next连成一个线性集合。
     */
        Entry<K,V> next;
        final int hash;
      /**
         * Creates new entry.
         */

        Entry(int h, K k, V v, Entry<K,V> n) {
            value = v;
            next = n;
            key = k;
            hash = h;
        }
}

HashMap的put方法(增加元素):
public V put(K key, V value) {
        if (key == null)
//可以以null为key
            return putForNullKey(value);
        
int hash = hash(key.hashCode());//算出此key的hash code,用位移的算法
        
int i = indexFor(hash, table.length);//用hashcode&table长度,即将hashcode的高位清零,保留低位(与table长度一样)的位,这样得到的下标,一定在table的长度范围之内。
        
          //找到table下标为i的位置,如果此位置已经有元素即两个元素的Key的hashcode相同,则说明此散列桶有多个元素
          //从第一个Entry开始,循环e.next遍历此链表并判断:
          for (Entry<K,V> e = table[i]; e != null; e = e.next) {
            Object k;
               //如果两个Entry的hashcoe相同,且equals,那么则认为是同一个Key
            
if (e.hash == hash && ((k = e.key) == key || key.equals(k))) {
                V oldValue = e.value;
                    //则把这个新元素替换原来的元素
                e.value = value;
                e.recordAccess(
this);
                    //并返回原来的元素
                
return oldValue;
            }
        }
          //如果table下标为i的位置为null,则说明此散列桶为空,直接将新元素添加到此即可
        modCount++;
        addEntry(hash, key, value, i);*注
        
return null;
}

*注:
 void addEntry(int hash, K key, V value, int bucketIndex) {
     Entry<K,V> e = table[bucketIndex];//e为null
     table[bucketIndex] = new Entry<K,V>(hash, key, value, e);//新建Entry,next为null
     if (size++ >= threshold)//如果实际大小超过阀值,则扩容
         resize(2 * table.length);
 }

这是小弟在优快云发的第一篇博文,如有技术上的错误,请大虾们不吝赐教!

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值