Hashmap是一种非常常用的、应用广泛的数据类型。
根据java.util.HashMap类源码可知,其数据模型实质为数组和链表的结合体。
下面我们由put方法为入口探究其结构和原理:
1.public V put(K key, V value)
/**
* Associates the specified value with the specified key in this map.
* If the map previously contained a mapping for the key, the old
* value is replaced.
*
* @param key key with which the specified value is to be associated
* @param value value to be associated with the specified key
* @return the previous value associated with <tt>key</tt>, or
* <tt>null</tt> if there was no mapping for <tt>key</tt>.
* (A <tt>null</tt> return can also indicate that the map
* previously associated <tt>null</tt> with <tt>key</tt>.)
*/
public V put(K key, V value) {
return putVal(hash(key), key, value, false, true);
}
由代码可知,put方法调用putVal,其中入参hash(int)传入key的哈希值。
2.final V putVal(int hash, K key, V value, boolean onlyIfAbsent,
boolean evict)
/**
* Implements Map.put and related methods
*
* @param hash hash for key
* @param key the key
* @param value the value to put
* @param onlyIfAbsent if true, don't change existing value
* @param evict if false, the table is in creation mode.
* @return previous value, or null if none
*/
final V putVal(int hash, K key, V value, boolean onlyIfAbsent,
boolean evict) {
Node<K,V>[] tab; Node<K,V> p; int n, i;
if ((tab = table) == null || (n = tab.length) == 0)<span style="font-family:SimSun;">//如果数组table为null或者长度为0</span>
n = (tab = resize()).length;<span style="font-family:SimSun;">//调用resize()方法初始化table</span>
if ((p = tab[i = (n - 1) & hash]) == null)<span style="font-family:SimSun;">//判断原数组tab[hash]位置是否有值</span>
tab[i] = newNode(hash, key, value, null);<span style="font-family:SimSun;">//如果没有值则生成node对象,并将引用存于tab[hash]</span>
else {
Node<K,V> e; K k;
if (p.hash == hash &&
((k = p.key) == key || (key != null && key.equals(k))))<span style="font-family:SimSun;">//判断:</span>传入<span style="font-family:SimSun;">哈希值与key均与table[hash]中匹配</span>
e = p;<span style="font-family:SimSun;">//获取该节点引用</span>
else if (p instanceof TreeNode)<span style="font-family:SimSun;">//如果p类型为TreeNode(LinkedHashMap中所用)</span>
e = ((TreeNode<K,V>)p).putTreeVal(this, tab, hash, key, value);//TreeNo<span style="font-family:SimSun;">de中方法,用于同hash的存储TreeNode,此处不做拓展</span>
else {
for (int binCount = 0; ; ++binCount) {
if ((e = p.next) == null) {<span style="font-family:SimSun;">//e获取p.next的引用</span>
p.next = newNode(hash, key, value, null);
if (binCount >= TREEIFY_THRESHOLD - 1) // -1 for 1st
treeifyBin(tab, hash);
break;
}
if (e.hash == hash &&
((k = e.key) == key || (key != null && key.equals(k))))
break;
p = e;
}
}
if (e != null) { // existing mapping for key
V oldValue = e.value;
if (!onlyIfAbsent || oldValue == null)
e.value = value;
afterNodeAccess(e);<span style="font-family:SimSun;">//LinkedHashMap中所用,此处不作拓展</span>
return oldValue;<span style="font-family:SimSun;">//返回旧值</span>
}
}
++modCount;
if (++size > threshold)
resize();
afterNodeInsertion(evict);
return null;
}
*上述代码注释为笔者所加,所述如有误请大家指正
根据putVal方法源码可知,其操作为:
1.找到table[hash]位置,查看是否存有对Node对象的引用,如果为null,则根据入参Node对象,将其引用存于table[hash]
2.如果上述位置存在对Node对象的引用,则比对传入key与该Node中所存key是否相同,如果相同则获取该Node对象的引用;否则