讨论源码环境为JDK1.8 211
HashTable如何计算数组下标?
首先我们看看String的hashCode是如何计算的(出自JDK1.8.0 211 java.lang.String 1452行—1476行)
/**
* Returns a hash code for this string. The hash code for a
* {@code String} object is computed as
* <blockquote><pre>
* s[0]*31^(n-1) + s[1]*31^(n-2) + ... + s[n-1]
* </pre></blockquote>
* using {@code int} arithmetic, where {@code s[i]} is the
* <i>i</i>th character of the string, {@code n} is the length of
* the string, and {@code ^} indicates exponentiation.
* (The hash value of the empty string is zero.)
*
* @return a hash code value for this object.
*/
public int hashCode() {
int h = hash;
if (h == 0 && value.length > 0) {
char val[] = value;
for (int i = 0; i < value.length; i++) {
h = 31 * h + val[i];
}
hash = h;
}
return h;
}
String类重写了hashCode()函数,计算字符串的hashCode的公式为:
s
[
0
]
∗
3
1
n
−
1
+
s
[
1
]
∗
3
1
n
−
2
+
.
.
.
+
s
[
n
−
1
]
s[0]*31^{n-1}+s[1]*31^{n-2}+...+s[n-1]
s[0]∗31n−1+s[1]∗31n−2+...+s[n−1]
我们来举个例子:
经过debug我们得知三个字符串的hashCode()返回值,我们来通过计算公式模拟一下:
字符串“a”,长度n=1,字符‘a’=97,代入公式得:
s
[
0
]
∗
3
1
n
−
1
+
s
[
1
]
∗
3
1
n
−
2
+
.
.
.
+
s
[
n
−
1
]
=
97
∗
3
1
1
−
1
=
97
s[0]*31^{n-1}+s[1]*31^{n-2}+...+s[n-1]=97*31^{1-1}=97
s[0]∗31n−1+s[1]∗31n−2+...+s[n−1]=97∗311−1=97
字符串“ab”,长度n=2,字符‘a’=97,‘b’=98,代入公式得:
s
[
0
]
∗
3
1
n
−
1
+
s
[
1
]
∗
3
1
n
−
2
+
.
.
.
+
s
[
n
−
1
]
=
97
∗
3
1
2
−
1
+
98
∗
3
1
1
−
1
=
3105
s[0]*31^{n-1}+s[1]*31^{n-2}+...+s[n-1]=97*31^{2-1}+98*31^{1-1}=3105
s[0]∗31n−1+s[1]∗31n−2+...+s[n−1]=97∗312−1+98∗311−1=3105
我们知道了String对象的hashCode是如何产生的了,接着我们看HashTable源码,看看它的数组下标是如何计算的:(代码截取自JDK1.8 211 java.util.HashTable.java 457行—479行)
public synchronized V put(K key, V value) {
// Make sure the value is not null
if (value == null) {
throw new NullPointerException();
}
// Makes sure the key is not already in the hashtable.
Entry<?,?> tab[] = table;
int hash = key.hashCode();
int index = (hash & 0x7FFFFFFF) % tab.length;
@SuppressWarnings("unchecked")
Entry<K,V> entry = (Entry<K,V>)tab[index];
for(; entry != null ; entry = entry.next) {
if ((entry.hash == hash) && entry.key.equals(key)) {
V old = entry.value;
entry.value = value;
return old;
}
}
addEntry(hash, key, value, index);
return null;
}
看这几行:
Entry<?,?> tab[] = table;
int hash = key.hashCode();
int index = (hash & 0x7FFFFFFF) % tab.length;
tab是HashTable底层的数组;
hash是key对象的hashCode();
index是通过key的hashCode()返回值计算出来的数组下标。
hashCode()返回值与2^31-1=0x7FFFFFFF(31位1)进行与(&)操作,然后除以数组长度取余,得到的就是下标值。
总结一下:HashTable计算数组下标值的时候,先获取key的hashCode()值(int类型),然后与int最大值进行与(&)操作,最后直接除以数组长度取余,得到数组下标。