2.HashMap 中 Key 的 index 是怎样计算的?
HashMap中的 table 是怎样确定数组索引位置的?
对于HashMap内的所有实现来说,首先第一步是定位对键值对所在数组的索引下标位置,这是后续所有操作的基础.
如下代码是展示索引下标获取的基本逻辑:
/* ---------------- Static utilities -------------- */
/** * Computes key.hashCode() and spreads (XORs) higher bits of hash * to lower. Because the table uses power-of-two masking, sets of * hashes that vary only in bits above the current mask will * always collide. (Among known examples are sets of Float keys * holding consecutive whole numbers in small tables.) So we * apply a transform that spreads the impact of higher bits * downward. There is a tradeoff between speed, utility, and * quality of bit-spreading. Because many common sets of hashes * are already reasonably distributed (so don't benefit from * spreading), and because we use trees to handle large sets of * collisions in bins, we just XOR some shifted bits in the * cheapest possible way to reduce systematic lossage, as well as * to incorporate impact of the highest bits that would otherwise * never be used in index calculations because of table bounds. */ static final int hash(Object key) { int h; return (key == null) ? 0 : (h = key.hashCode()) ^ (h >>> 16); }</code></pre></div></div><p>然后, 数组元素的下标算法是:</p><div class="rno-markdown-code"><div class="rno-markdown-code-toolbar"><div class="rno-markdown-code-toolbar-info"><div class="rno-markdown-code-toolbar-item is-type"><span class="is-m-hidden">代码语言:</span>javascript</div></div><div class="rno-markdown-code-toolbar-opt"><div class="rno-markdown-code-toolbar-copy"><i class="icon-copy"></i><span class="is-m-hidden">复制</span></div></div></div><div class="developer-code-block"><pre class="prism-token token line-numbers language-javascript"><code class="language-javascript" style="margin-left:0"> public static final int index(int hash, int n) { return (n - 1) & hash; }</code></pre></div></div><p>代码拆解:</p><div class="rno-markdown-code"><div class="rno-markdown-code-toolbar"><div class="rno-markdown-code-toolbar-info"><div class="rno-markdown-code-toolbar-item is-type"><span class="is-m-hidden">代码语言:</span>javascript</div></div><div class="rno-markdown-code-toolbar-opt"><div class="rno-markdown-code-toolbar-copy"><i class="icon-copy"></i><span class="is-m-hidden">复制</span></div></div></div><div class="developer-code-block"><pre class="prism-token token line-numbers language-javascript"><code class="language-javascript" style="margin-left:0"> public static final int hash(Object key) { if (key == null) { return 0; } int h = key.hashCode(); int rh = h >>> 16; out.println("===================== hash(" + key + ") ==========================="); out.println("key \t\t" + key); out.println("h=key.hashCode() \t\t" + toBinary(h) + "\t\t" + h); out.println("rh=h>>>16 \t\t" + toBinary(rh) + "\t\t" + rh); return h ^ rh; } public static final int index(int hash, int n) { out.println("hash=h ^ rh \t\t" + toBinary(hash) + "\t\t" + hash); out.println("n = \t\t" + toBinary(n) + "\t\t" + (n)); out.println("n - 1= \t\t" + toBinary(n - 1) + "\t\t" + (n - 1)); int index = (n - 1) & hash; out.println("index=(n - 1) & hash \t\t" + toBinary(index) + "\t\t" + index); out.println(); return index; }</code></pre></div></div><p>运行测试数据:</p><div class="rno-markdown-code"><div class="rno-markdown-code-toolbar"><div class="rno-markdown-code-toolbar-info"><div class="rno-markdown-code-toolbar-item is-type"><span class="is-m-hidden">代码语言:</span>javascript</div></div><div class="rno-markdown-code-toolbar-opt"><div class="rno-markdown-code-toolbar-copy"><i class="icon-copy"></i><span class="is-m-hidden">复制</span></div></div></div><div class="developer-code-block"><pre class="prism-token token line-numbers language-javascript"><code class="language-javascript" style="margin-left:0">===================== hash(a) ===========================
key a
h=key.hashCode() 00000000000000000000000001100001 97
rh=h>>>16 00000000000000000000000000000000 0
hash(a)=97
hash=h ^ rh 00000000000000000000000001100001 97
n = 00000000000000000000000000000001 1
n - 1= 00000000000000000000000000000000 0
index=(n - 1) & hash 00000000000000000000000000000000 0===================== hash(b) ===========================
key b
h=key.hashCode() 00000000000000000000000001100010 98
rh=h>>>16 00000000000000000000000000000000 0
hash(b)=98
hash=h ^ rh 00000000000000000000000001100010 98
n = 00000000000000000000000000000010 2
n - 1= 00000000000000000000000000000001 1
index=(n - 1) & hash 00000000000000000000000000000000 0===================== hash(abc) ===========================
key abc
h=key.hashCode() 00000000000000010111100001100010 96354
rh=h>>>16 00000000000000000000000000000001 1
hash(abc)=96355
hash=h ^ rh 00000000000000010111100001100011 96355
n = 00000000000000000000000000000100 4
n - 1= 00000000000000000000000000000011 3
index=(n - 1) & hash 00000000000000000000000000000011 3===================== hash(abcde) ===========================
key abcde
h=key.hashCode() 00000101100001001111010001100011 92599395
rh=h>>>16 00000000000000000000010110000100 1412
hash(abcde)=92598759
hash=h ^ rh 00000101100001001111000111100111 92598759
n = 00000000000000000000000000001000 8
n - 1= 00000000000000000000000000000111 7
index=(n - 1) & hash 00000000000000000000000000000111 7
===================== hash(abcdefg) ===========================
key abcdefg
h=key.hashCode() 10111000000110010111010001100100 -1206291356
rh=h>>>16 00000000000000001011100000011001 47129
hash(abcdefg)=-1206268803
hash=h ^ rh 10111000000110011100110001111101 -1206268803
n = 00000000000000000000000000001000 8
n - 1= 00000000000000000000000000000111 7
index=(n - 1) & hash 00000000000000000000000000000101 5
算法图解:
源码分析:
/**
* Returns the value to which the specified key is mapped,
* or {@code null} if this map contains no mapping for the key.
*
* <p>More formally, if this map contains a mapping from a key
* {@code k} to a value {@code v} such that {@code (key==null ? k==null :
* key.equals(k))}, then this method returns {@code v}; otherwise
* it returns {@code null}. (There can be at most one such mapping.)
*
* <p>A return value of {@code null} does not <i>necessarily</i>
* indicate that the map contains no mapping for the key; it's also
* possible that the map explicitly maps the key to {@code null}.
* The {@link #containsKey containsKey} operation may be used to
* distinguish these two cases.
*
* @see #put(Object, Object)
*/
public V get(Object key) {
Node<K,V> e;
return (e = getNode(hash(key), key)) == null ? null : e.value;
}
/**
* Implements Map.get and related methods
*
* @param hash hash for key
* @param key the key
* @return the node, or null if none
*/
final Node<K,V> getNode(int hash, Object key) {
Node<K,V>[] tab; Node<K,V> first, e; int n; K k;
if ((tab = table) != null && (n = tab.length) > 0 &&
(first = tab[(n - 1) & hash]) != null) { // index = (n - 1) & hash
if (first.hash == hash && // always check first node
((k = first.key) == key || (key != null && key.equals(k))))
return first;
if ((e = first.next) != null) {
if (first instanceof TreeNode)
return ((TreeNode<K,V>)first).getTreeNode(hash, key);
do {
if (e.hash == hash &&
((k = e.key) == key || (key != null && key.equals(k))))
return e;
} while ((e = e.next) != null);
}
}
return null;
}</code></pre></div></div><h3 id="25n5t" name="HashMap-%E4%B8%AD%E7%9A%84-tableSizeFor()-%E6%96%B9%E6%B3%95%E8%AF%A6%E8%A7%A3">HashMap 中的 tableSizeFor() 方法详解</h3><p>输入: int cap;
返回: n = tableSizeFor(cap)
其中, n % 2 ==0, and cap <=n.
代码语言:javascript复制 /**
* Returns a power of two size for the given target capacity.
*/
static final int tableSizeFor(int cap) {
// 先 cap 减 1
int n = cap - 1;
n |= n >>> 1;
n |= n >>> 2;
n |= n >>> 4;
n |= n >>> 8;
n |= n >>> 16;
return (n < 0) ? 1 :
(n >= MAXIMUM_CAPACITY) ? MAXIMUM_CAPACITY : n + 1; // 再加 1
}
我们来运行测试一下:
代码语言:javascript复制===============tableSizeFor(2)==================
cap = 2
n = cap - 1 00000000000000000000000000000001
n = n | (n >>> 1) 00000000000000000000000000000001
n = n | (n >>> 2) 00000000000000000000000000000001
n = n | (n >>> 4) 00000000000000000000000000000001
n = n | (n >>> 8) 00000000000000000000000000000001
n = n | (n >>> 16) 00000000000000000000000000000001
tableSizeFor = 2
2
===============tableSizeFor(5)==================
cap = 5
n = cap - 1 00000000000000000000000000000100
n = n | (n >>> 1) 00000000000000000000000000000110
n = n | (n >>> 2) 00000000000000000000000000000111
n = n | (n >>> 4) 00000000000000000000000000000111
n = n | (n >>> 8) 00000000000000000000000000000111
n = n | (n >>> 16) 00000000000000000000000000000111
tableSizeFor = 8
8
===============tableSizeFor(10)==================
cap = 10
n = cap - 1 00000000000000000000000000001001
n = n | (n >>> 1) 00000000000000000000000000001101
n = n | (n >>> 2) 00000000000000000000000000001111
n = n | (n >>> 4) 00000000000000000000000000001111
n = n | (n >>> 8) 00000000000000000000000000001111
n = n | (n >>> 16) 00000000000000000000000000001111
tableSizeFor = 16
16
===============tableSizeFor(25)==================
cap = 25
n = cap - 1 00000000000000000000000000011000
n = n | (n >>> 1) 00000000000000000000000000011100
n = n | (n >>> 2) 00000000000000000000000000011111
n = n | (n >>> 4) 00000000000000000000000000011111
n = n | (n >>> 8) 00000000000000000000000000011111
n = n | (n >>> 16) 00000000000000000000000000011111
tableSizeFor = 32
32
===============tableSizeFor(58)==================
cap = 58
n = cap - 1 00000000000000000000000000111001
n = n | (n >>> 1) 00000000000000000000000000111101
n = n | (n >>> 2) 00000000000000000000000000111111
n = n | (n >>> 4) 00000000000000000000000000111111
n = n | (n >>> 8) 00000000000000000000000000111111
n = n | (n >>> 16) 00000000000000000000000000111111
tableSizeFor = 64
64
===============tableSizeFor(100)==================
cap = 100
n = cap - 1 00000000000000000000000001100011
n = n | (n >>> 1) 00000000000000000000000001110011
n = n | (n >>> 2) 00000000000000000000000001111111
n = n | (n >>> 4) 00000000000000000000000001111111
n = n | (n >>> 8) 00000000000000000000000001111111
n = n | (n >>> 16) 00000000000000000000000001111111
tableSizeFor = 128
128
===============tableSizeFor(896)==================
cap = 896
n = cap - 1 00000000000000000000001101111111
n = n | (n >>> 1) 00000000000000000000001111111111
n = n | (n >>> 2) 00000000000000000000001111111111
n = n | (n >>> 4) 00000000000000000000001111111111
n = n | (n >>> 8) 00000000000000000000001111111111
n = n | (n >>> 16) 00000000000000000000001111111111
tableSizeFor = 1024
1024
===============tableSizeFor(1073741000)==================
cap = 1073741000
n = cap - 1 00111111111111111111110011000111
n = n | (n >>> 1) 00111111111111111111111011100111
n = n | (n >>> 2) 00111111111111111111111111111111
n = n | (n >>> 4) 00111111111111111111111111111111
n = n | (n >>> 8) 00111111111111111111111111111111
n = n | (n >>> 16) 00111111111111111111111111111111
tableSizeFor = 1073741824
1073741824
参考资料
https://blog.csdn.net/fan2012huan/article/details/51097331
https://www.jianshu.com/p/cbe3f22793be
https://www.jianshu.com/p/dbbecc36200d