HashMap底层的实现原理和扩容机制

1.是什么

HashMap是基于哈希表实现的,它存储键值对(key-value pairs)。以下是HashMap的主要实现原理:

1. 哈希桶数组(Buckets)

        HashMap内部维护了一个叫做“哈希桶”的数组,数组的每个槽位对应一个桶,用于存放键值对。

2. 键的哈希值

        当向HashMap中插入一个键值对时,首先会计算键的哈希值。这个哈希值决定了键值对应该存储在哪个桶中。

3. 索引计算

          HashMap使用哈希值与数组长度的位运算来确定键值对在数组中的索引位置。例如,如果数组的长度是16,那么键的哈希值会与15(即16-1)进行位与操作(hash & (length - 1)),这样可以保证结果在0到15之间。

4. 处理哈希碰撞

        如果两个不同的键产生了相同的索引,这称为哈希碰撞。HashMap通过链表来解决哈希碰撞。在Java 8及以后的版本中,当链表的长度超过一定阈值时,链表会被转换成红黑树,以优化搜索性能。

以下是HashMap插入操作的简化步骤:

public V put(K key, V value) {
    int hash = hash(key); // 计算哈希值
    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;
        }
    }
    addEntry(hash, key, value, i); // 添加新节点到链表或树中
    return null;
}

HashMap的扩容机制

        HashMap的扩容机制是当哈希桶数组中的元素达到一定的比例时,会进行扩容操作,以保持HashMap的性能。以下是扩容的主要步骤:

1. 装载因子(Load Factor)

装载因子是衡量HashMap满的程度的一个标准。它的默认值是0.75,这意味着当HashMap中的元素数量达到容量的75%时,就会触发扩容。

2. 扩容阈值

扩容阈值是HashMap能够容纳的最大元素数量。这个值等于数组的长度乘以装载因子。当HashMap中的元素数量达到这个阈值时,就会触发扩容。

3. 扩容操作

扩容操作包括以下步骤:

  • 创建一个新的哈希桶数组,其大小是原数组大小的两倍。
  • 遍历原数组中的所有元素,并将它们重新插入到新的数组中。因为数组的长度变了,所以每个元素的位置都需要重新计算。

以下是扩容操作的简化代码:

void resize(int newCapacity) {
    Entry[] oldTable = table;
    int oldCapacity = oldTable.length;
    if (oldCapacity == MAXIMUM_CAPACITY) {
        threshold = Integer.MAX_VALUE;
        return;
    }
    
    Entry[] newTable = new Entry[newCapacity];
    transfer(newTable); // 重新计算所有元素在新数组中的位置
    table = newTable;
    threshold = (int)(newCapacity * loadFactor); // 更新扩容阈值
}

void transfer(Entry[] newTable) {
    Entry[] src = table;
    int newCapacity = newTable.length;
    for (int j = 0; j < src.length; j++) {
        Entry<K,V> e = src[j];
        if (e != null) {
            src[j] = null;
            do {
                Entry<K,V> next = e.next;
                int i = indexFor(e.hash, newCapacity); // 重新计算索引位置
                e.next = newTable[i];
                newTable[i] = e;
                e = next;
            } while (e != null);
        }
    }
}

        扩容是一个比较昂贵的操作,因为它需要重新计算所有元素的哈希值并将它们重新插入到新的数组中。因此,如果预先知道HashMap将存储大量元素,最好在创建HashMap时就指定一个足够大的初始容量,以减少扩容的次数。

哈希表(Hashmap)是一种常见的数据结构,它的底层实现原理主要基于哈希函数和数组。当插入、查找或删除元素时,首先通过哈希函数将键(key)转换为一个索引值,然后在对应的数组位置存储或访问值。这种操作通常具有常数时间复杂度O(1),但在哈希冲突较多时效率会下降。 哈希冲突是指不同的键可能计算出相同的索引。处理哈希冲突的方法有很多种,最常见的有两种: 1. **开放寻址法**(Open Addressing):遇到冲突时,寻找下一个空闲的位置存放元素,直到找到合适的为止。比如线性探测(Linear Probing)、二次探测(Quadratic Probing)或双散列(Double Hashing)。 2. **链地址法**(Separate Chaining):每个数组元素不再直接存储值,而是指向一个链表,将所有冲突的元素放在相应的链表中。当我们按索引查找时,如果该位置不是我们想要的值,就沿着链接链表继续查找。 扩容机制是为了应对数据增加导致的哈希冲突增多。当哈希表的装载因子(已存储元素数量/总容量)超过预设阈值,通常为0.75或0.8,系统会选择扩大哈希表的大小,新表通常是原表的两倍。扩容的具体步骤包括: - 创建一个新的更大的哈希表。 - 遍历旧表中的每一个元素,重新计算新的键对应的索引,并把元素插入到新表的相应位置。 - 将旧表的数据迁移至新表。 - 更新旧表为只读状态,或者销毁旧表以释放内存。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值