HashMap源码分析,看一遍就懂!("轻松掌握HashMap源码解析,一读即懂!")

原创
ithorizon 7个月前 (10-19) 阅读数 24 #后端开发

轻松掌握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能够减成本时间程序的性能和可维护性。


本文由IT视界版权所有,禁止未经同意的情况下转发

文章标签: 后端开发


热门