hashMap详解

本文详细解读了HashMap数据结构的存储方式,包括数组+单链表组合的内部实现,以及如何通过hash算法高效定位元素,解决冲突问题,同时介绍了Entry类作为key-value对的存储单元,以及其在链表中的作用。

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

对于hashMap而言,它的存储方式比ArrayList要复杂一点:采用一种所谓的hash算法来决定每个元素的位置
HashMap中的数据结构是数组+单链表的组合,我们希望的是元素存放的更均匀,最理想的效果是,Entry数组中每个位置都只有一个元素,这样,查询的时候效率最高,不需要遍历单链表,也不需要通过equals去比较K,而且空间利用率最大。
HashMap类的put(K key,V value)方法的源代如下:

```
 public V put(K key, V value) {
    if (table == EMPTY_TABLE) {
        inflateTable(threshold);
    }
    //如果key为null,调用putForNullKey进行处理
    if (key == null)
        return putForNullKey(value);
        //根据key的hashCode计算hash值
    int hash = hash(key);
    //搜索hash值在对应的table里面的索引
    int i = indexFor(hash, table.length);
    //如果i处索引的Entry不为空,通过循环不断遍历e元素的下一个元素
    for (Entry<K,V> e = table[i]; e != null; e = e.next) {
        Object k;
        //找到key于需要放入的key相等,hash值相同,通过equals比较返回true
        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;
}
```

###
上面的程序用到了一个非常重要的接口Map.Entry.每个Map.Entry其实就是一个key-value对,当系统需要存储HashMap里面的key-value对的时候完全没有去考虑Entry里面的value,而仅仅是根据key来计算并且决定每个Entry的存储位置,因此可以完全的把value当成key的附属,当系统决定了key的时候,value随之保存在哪里即可。
上面还涉及到了一个addEntry()方法,该方法用于添加一个key-value对。

```
   void addEntry(int hash, K key, V value, int bucketIndex) {
     //如果map里面的key-value对的数量超过了极限,并且当前的table索引处的Entry不为空
    if ((size >= threshold) && (null != table[bucketIndex])) {
        resize(2 * table.length);//将table的长度扩充为原来的2倍
        //计算当前key的hash值
        hash = (null != key) ? hash(key) : 0;
        //根据hash,找到该指定的hash值在对应的table里面的索引
        bucketIndex = indexFor(hash, table.length);
    }
   //调用 createEntry方法把该entry对象添加到指定的索引处
    createEntry(hash, key, value, bucketIndex);
}
```

###

下面进一步的分析下: createEntry(hash, key, value, bucketIndex);方法

```
void createEntry(int hash, K key, V value, int bucketIndex) {
    //找到指定的bucketIndex处的Entry
    Entry<K,V> e = table[bucketIndex];
    //把新建的entry放入到bucketIndex索引处,并让新的Entry指向原来的entry
    table[bucketIndex] = new Entry<>(hash, key, value, e);
    size++;
}
```
Hash的底层是基于Entry[]数组来进行存储的,
为此补充下Enrty的定义
 1 static class Entry<K,V> implements Map.Entry<K,V> {
 2         final K key ; 
 3         V value;
 4         Entry<K,V> next; // 指向下一个节点
 5         final int hash;
 6 
 7         Entry( int h, K k, V v, Entry<K,V> n) {
 8             value = v;
 9             next = n;
10             key = k;
11             hash = h;
12         }
13 
14         public final K getKey() {
15             return key ;
16         }
17 
18         public final V getValue() {
19             return value ;
20         }
21 
22         public final V setValue(V newValue) {
23            V oldValue = value;
24             value = newValue;
25             return oldValue;
26         }
27 
28         public final boolean equals(Object o) {
29             if (!(o instanceof Map.Entry))
30                 return false;
31             Map.Entry e = (Map.Entry)o;
32             Object k1 = getKey();
33             Object k2 = e.getKey();
34             if (k1 == k2 || (k1 != null && k1.equals(k2))) {
35                 Object v1 = getValue();
36                 Object v2 = e.getValue();
37                 if (v1 == v2 || (v1 != null && v1.equals(v2)))
38                     return true;
39             }
40             return false;
41         }
42 
43         public final int hashCode() {
44             return (key ==null   ? 0 : key.hashCode()) ^
45                    ( value==null ? 0 : value.hashCode());
46         }
47 
48         public final String toString() {
49             return getKey() + "=" + getValue();
50         }
51 
52         // 当向HashMap中添加元素的时候调用这个方法,这里没有实现是供子类回调用
53         void recordAccess(HashMap<K,V> m) {
54         }
55 
56         // 当从HashMap中删除元素的时候调动这个方法 ,这里没有实现是供子类回调用
57         void recordRemoval(HashMap<K,V> m) {
58         }
59 }

这里写图片描述

 Entry是HashMap的内部类,它继承了Map中的Entry接口,它定义了键(key),值(value),和下一个节点的引用(next),以及hash值。很明确的可以看出Entry是什么结构,它是单线链表的一个节点。也就是说HashMap的底层结构是一个数组,而数组的元素是一个单向链表。

题HashMap采用hash算法将key散列为一个int值,这个int值对应到数组的下标,再做查询操作的时候,拿到key的散列值,根据数组下标就能直接找到存储在数组的元素。但是由于hash可能会出现相同的散列值,为了解决冲突,HashMap采用将相同的散列值存储到一个链表中,也就是说在一个链表中的元素他们的散列值绝对是相同的。找到数组下标取出链表,再遍历链表。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值