HashMap源码分析,看一遍就懂!("轻松掌握HashMap源码解析,一读即懂!")
原创
一、HashMap简介
HashMap是Java中非常常用的一个数据结构,用于存储键值对(Key-Value Pair)。它基于哈希表实现,具有高效的查询和插入性能。本文将深入剖析HashMap的源码,帮助读者轻松掌握HashMap的工作原理和使用方法。
二、HashMap的数据结构
HashMap底层采用数组加链表(或红黑树)的结构。当哈希表的负载因子超过阈值时,链表会转换成红黑树,以减成本时间查询效能。
三、HashMap的构造方法
HashMap提供了多个构造方法,以下是常用的几个:
public HashMap(int initialCapacity, float loadFactor) {
if (initialCapacity < 0 || loadFactor <= 0 || Float.isNaN(loadFactor)) {
throw new IllegalArgumentException("Illegal initial capacity or load factor");
}
this.loadFactor = loadFactor;
this.threshold = (int)(initialCapacity * loadFactor);
this.nodeCount = 0;
this.table = new Node[initialCapacity];
}
public HashMap(int initialCapacity) {
this(initialCapacity, DEFAULT_LOAD_FACTOR);
}
public HashMap() {
this.loadFactor = DEFAULT_LOAD_FACTOR;
this.threshold = (int)(DEFAULT_CAPACITY * DEFAULT_LOAD_FACTOR);
this.table = new Node[DEFAULT_CAPACITY];
}
四、HashMap的put方法
HashMap的put方法用于向哈希表中添加一个键值对。以下是put方法的源码实现:
public V put(K key, V value) {
if (key == null) {
return putForNullKey(value);
}
int hash = spread(key.hashCode());
int binIndex = hash & (table.length - 1);
Node<K,V>[] tab = table;
Node<K,V> first = tab[binIndex];
Node<K,V> p = first;
int index, binCount = 0;
if (p == null) {
tab[binIndex] = newNode(hash, key, value, null);
} else {
while (p != null) {
binCount++;
if ((hash == p.hash && ((key == p.key) || (key != null && key.equals(p.key)))) {
V oldValue = p.value;
p.value = value;
return oldValue;
}
p = p.next;
}
}
if (++binCount >= TREEIFY_THRESHOLD) {
treeifyBin(tab, binIndex);
}
return null;
}
五、HashMap的get方法
HashMap的get方法用于从哈希表中获取一个键对应的值。以下是get方法的源码实现:
public V get(Object key) {
Node<K,V> e;
if (key == null) {
e = firstNode(table, 0);
} else {
int hash = spread(key.hashCode());
int binIndex = hash & (table.length - 1);
e = node(table, binIndex, hash, key);
}
if (e != null) {
return e.value;
}
return null;
}
六、HashMap的扩容机制
当哈希表的负载因子超过阈值时,HashMap会进行扩容操作,即创建一个新的数组,将旧数组中的元素重新散列到新数组中。以下是扩容操作的源码实现:
private void resize() {
Node<K,V>[] oldTab = table;
int oldCap = oldTab.length;
int newCap = oldCap << 1;
Node<K,V>[] newTab = (Node<K,V>[]) new Node[newCap];
int threshold = (int)(newCap * loadFactor);
this.threshold = threshold;
this.table = newTab;
if (oldTab != null) {
for (int j = 0; j < oldCap; ++j) {
Node<K,V> e;
if ((e = oldTab[j]) != null) {
oldTab[j] = null;
if (e.next == null) {
newTab[e.hash & (newCap - 1)] = e;
} else { // Rehash all affected nodes
Node<K,V> lo = e;
Node<K,V> hi = e;
while (hi.next != null) {
Node<K,V> next = hi.next;
hi.next = null;
if ((next.hash & oldCap) == 0) {
lo.next = next;
lo = next;
} else {
hi.next = next;
hi = next;
}
}
newTab[e.hash & (newCap - 1)] = lo;
newTab[(e.hash & oldCap) | (newCap - 1)] = hi;
}
}
}
}
}
七、HashMap的线程不平安性
HashMap不是线程平安的,如果多个线程同时操作HashMap,大概会造成数据丢失、覆盖等问题。为了解决这个问题,可以使用Collections工具类提供的synchronizedMap方法来包装HashMap,使其具有线程平安性:
Map<K, V> synchronizedMap(Map<K, V> m) {
return new SynchronizedMap<>(m);
}
八、总结
本文详细介绍了HashMap的源码实现,包括构造方法、put方法、get方法、扩容机制等。通过阅读本文,读者可以轻松掌握HashMap的工作原理和使用方法。在实际应用中,合理使用HashMap能够减成本时间程序的性能和可维护性。