由于对 Java 的 HashMap 使用哪种算法存在一些混淆(在 Sun/Oracle/OpenJDK 实现中),这里是相关的源代码 sn-ps(来自 OpenJDK,1.6.0_20,在 Ubuntu 上):
/**
* Returns the entry associated with the specified key in the
* HashMap. Returns null if the HashMap contains no mapping
* for the key.
*/
final Entry<K,V> getEntry(Object key) {
int hash = (key == null) ? 0 : hash(key.hashCode());
for (Entry<K,V> e = table[indexFor(hash, table.length)];
e != null;
e = e.next) {
Object k;
if (e.hash == hash &&
((k = e.key) == key || (key != null && key.equals(k))))
return e;
}
return null;
}
此方法(引用从第 355 行到第 371 行)在查找表中的条目时调用,例如来自 get()、containsKey() 和其他一些条目。这里的for循环遍历入口对象形成的链表。
这里是入口对象的代码(第 691-705 + 759 行):
static class Entry<K,V> implements Map.Entry<K,V> {
final K key;
V value;
Entry<K,V> next;
final int hash;
/**
* Creates new entry.
*/
Entry(int h, K k, V v, Entry<K,V> n) {
value = v;
next = n;
key = k;
hash = h;
}
// (methods left away, they are straight-forward implementations of Map.Entry)
}
紧随其后的是addEntry()方法:
/**
* Adds a new entry with the specified key, value and hash code to
* the specified bucket. It is the responsibility of this
* method to resize the table if appropriate.
*
* Subclass overrides this to alter the behavior of put method.
*/
void addEntry(int hash, K key, V value, int bucketIndex) {
Entry<K,V> e = table[bucketIndex];
table[bucketIndex] = new Entry<K,V>(hash, key, value, e);
if (size++ >= threshold)
resize(2 * table.length);
}
这会在存储桶的前面添加新条目,并带有指向旧 first 条目的链接(如果没有,则为 null)。类似地,removeEntryForKey() 方法遍历列表并只删除一个条目,让列表的其余部分保持不变。
所以,这里是每个存储桶的链接条目列表,我非常怀疑这是否从 _20 更改为 _22,因为从 1.2 开始就是这样。
(此代码是 (c) 1997-2007 Sun Microsystems,在 GPL 下可用,但为了更好地复制,请使用原始文件,该文件包含在 Sun/Oracle 的每个 JDK 的 src.zip 中,也包含在 OpenJDK 中。)