返回
揭开单线程HashMap的神秘面纱,3分钟轻松GET!
后端
2024-01-22 23:04:24
在程序员的工具箱中,HashMap可谓是一颗闪耀的明星,它是一种高效的键值对数据结构,广泛应用于各类场景。作为一名技术博客专家,我将带你深入浅出地领略单线程HashMap的魅力。
基础概念:
HashMap本质上是一个哈希表,它使用哈希函数将键映射到一个特定的桶(bucket)中。每个桶通常是一个链表,用来存储具有相同哈希值的键值对。为了防止哈希冲突(多个键映射到同一个桶),HashMap会在达到一定负载因子时进行扩容。
源码解读:
public class HashMap<K, V> {
private transient Node<K,V>[] table;
private transient int size;
private transient int threshold;
private final float loadFactor;
public HashMap() {
this(16, 0.75f);
}
public HashMap(int initialCapacity, float loadFactor) {
this.loadFactor = loadFactor;
this.threshold = (int) (initialCapacity * loadFactor);
table = new Node[initialCapacity];
}
public V get(Object key) {
int hash = key.hashCode();
int i = indexFor(hash, table.length);
Node<K,V> e = table[i];
while (e != null) {
if (e.hash == hash && e.key.equals(key)) {
return e.value;
}
e = e.next;
}
return null;
}
public V put(K key, V value) {
int hash = key.hashCode();
int i = indexFor(hash, table.length);
for (Node<K,V> e = table[i]; e != null; e = e.next) {
if (e.hash == hash && e.key.equals(key)) {
V oldValue = e.value;
e.value = value;
return oldValue;
}
}
addEntry(hash, key, value, i);
return null;
}
private void addEntry(int hash, K key, V value, int bucketIndex) {
Node<K,V> newNode = new Node<>(hash, key, value, null);
Node<K,V> e = table[bucketIndex];
table[bucketIndex] = newNode;
newNode.next = e;
if (size++ >= threshold) {
resize(2 * table.length);
}
}
}
工作原理:
- 哈希函数: HashMap利用哈希函数将键映射到一个桶中,桶的索引通过
indexFor
方法计算得到。 - 链表存储: 每个桶是一个链表,用来存储具有相同哈希值的键值对。当发生哈希冲突时,新插入的键值对会被添加到链表的尾部。
- 扩容机制: 当HashMap中的元素数量达到一定比例(负载因子)时,会触发扩容操作。扩容会创建一个新的、更大的哈希表,并将原有元素重新哈希分配到新的表中。
- 链表转红黑树: 在JDK1.8中,HashMap对链表做了优化。当链表长度超过8时,会将链表转换为红黑树,红黑树是一种具有自平衡特性的二叉搜索树,可以提高查找和插入的效率。
总结:
单线程HashMap是一种高效的数据结构,它使用哈希函数和链表(或红黑树)来快速存储和检索键值对。其背后的原理并不复杂,通过结合源码和清晰的解释,我们可以轻松地理解其工作机制。