一、简单叙述
HashMap是Java中常用的一种数据结构,它以键值对的形式存储数据,具有高效的查找、插入和删除操作。本文将详细介绍HashMap的底层实现原理,包括哈希技术、底层数据结构和扩容机制,帮助读者深入理解HashMap的工作原理。
HashMap是Java集合框架中的一部分,它基于哈希表实现,允许使用任何对象作为键来存储和检索值。HashMap是非同步的,如果多个线程同时访问并至少有一个线程修改了HashMap,则必须在外部同步。
底层实现
代码语言:javascript复制public class HashMap<K, V> {
static class Node<K, V> {
final int hash;
final K key;
V value;
Node<K, V> next;
Node(int hash, K key, V value, Node<K, V> next) {
this.hash = hash;
this.key = key;
this.value = value;
this.next = next;
}
}
// 其他代码...
}
二、哈希技术
- 哈希函数
哈希函数是一种将任意长度的数据映射为固定长度数据的算法。在HashMap中,哈希函数的作用是将键映射到一个索引位置,以便快速查找和存储键值对。
- 哈希冲突
当两个或多个键的哈希值相同时,它们将映射到同一个索引位置,这种现象称为哈希冲突。HashMap使用链表和红黑树来解决哈希冲突,确保每个索引位置只存储一个键值对。
三、HashMap的底层实现
- 数据结构
HashMap底层采用数组 链表 红黑树的数据结构实现。数组是HashMap的主体,用于存储键值对;链表用于解决哈希冲突;红黑树是在链表长度超过一定阈值(默认为8)时,将链表转换为红黑树,以提高查找效率。
- 存储结构
HashMap的存储结构是一个Node类型的数组,Node是一个内部类,实现了Map.Entry接口。每个Node对象包含四个属性:key(键)、value(值)、hash(哈希值)和next(指向下一个Node的指针)。当发生哈希冲突时,新的键值对将被添加到链表中。
四、扩容机制
- 什么时候扩容
当HashMap中的元素数量达到数组大小的加载因子(默认为0.75)时,会触发扩容操作。加载因子是一个阈值,用于控制数组的大小和扩容的时机。加载因子越大,数组的空间利用率越高,但冲突的概率也越大;加载因子越小,数组的空间利用率越低,但冲突的概率也越小。因此,选择合适的加载因子可以平衡空间利用率和冲突概率。
- 如何扩容
扩容操作包括两个步骤:创建新的数组和重新计算键的哈希值。首先,HashMap会创建一个新的数组,其大小是原数组大小的两倍。然后,HashMap会遍历原数组中的每个元素,重新计算键的哈希值,并将键值对存储到新的数组中。在重新计算哈希值时,HashMap会使用一个特殊的算法来确保相同的键在新的数组中仍然具有相同的哈希值。这个算法称为“再哈希”。
代码语言:javascript复制void resize(int newCapacity) {
Node<K, V>[] oldTable = table;
int oldCapacity = oldTable.length;
if (oldCapacity == MAXIMUM_CAPACITY) {
threshold = Integer.MAX_VALUE;
return;
}
Node<K, V>[] newTable = new Node[newCapacity];
transfer(newTable);
table = newTable;
threshold = (int) (newCapacity * loadFactor);
}
五、总结
本文详细介绍了HashMap的底层实现原理,包括哈希技术、底层数据结构和扩容机制。HashMap是一种高效的数据结构,它使用哈希表实现键值对的存储和检索操作。通过深入了解HashMap的工作原理,我们可以更好地理解和使用它来解决实际问题。在实际开发中,我们需要根据具体情况选择合适的加载因子和初始容量来创建HashMap实例以提高性能和效率。
参考文章
谈谈我对HashMap扩容机制的理解及底层实现-CSDN博客