String hashCode() 文档与实现
String hashCode() documentation vs implementation
下面是 String.hashCode()
方法的源代码片段,来自 Java 8(准确地说是 1.8.0_131)
/**
* 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;
}
您可以看到,文档说,hashCode()
是使用以下公式计算的
s[0]*31^(n-1) + s[1]*31^(n-2) + ... + s[n-1]
虽然实际实现不同
for (int i = 0; i < value.length; i++) {
h = 31 * h + val[i];
}
我漏掉了什么明显的东西吗?请帮助我。
实施是正确的,需要注意的是可能会发生整数溢出(在这里没关系,它不会造成任何损害)。它使用 Horner's method 进行多项式计算。
下面是示例字符串的步骤 "CAT"。
h = 0
第一个循环:
i = 0
h = 31 * 0 + 'C' (67) = 67
第二个循环:
i = 1
h = 31 * 67 + 'A' (65) = 2142
第三个循环:
i = 2
h = 31 * 2142 + 'T' (84) = 66486
让我们从代码中推导出公式。这里,n 是 i
在字符串 s 中的索引。 for
循环的每次迭代都执行此公式。
hn = 31hn-1 + sn
h0 /* after loop i = 0 */ = s[0]
h1 /* after loop i = 1 */ = 31*h0 + s[1] = 31*s[0] + s[1]
h2 /* after loop i = 2 */ = 31*h1 + s[2] = 31*(31*s[0] + s[1]) + s[2]
h = 31*31*s[0] + 31*s[1] + s[2]
您看到的 31 次方的指数是因为每个循环在添加下一个字符的值之前乘以另一个 31
的因数。
通过一些例子最容易看出会发生什么。让我们采用长度为 n
的字符串 s
和上述所有符号。我们将分析迭代的循环迭代。我们将 h_old
称为 h
在当前迭代开始时的值,h_new
表示 h
在当前迭代结束时的值。很容易看出迭代 i
的 h_new
将是迭代 i + 1
.
的 h_old
╔═════╦════════════════════════════╦═════════════════════════════════════════════════╗
║ It. ║ h_old ║ h_new ║
╠═════╬════════════════════════════╬═════════════════════════════════════════════════╣
║ 1 ║ 0 ║ 31*h_old + s[0] = ║
║ ║ ║ s[0] ║
║ ║ ║ ║
║ 2 ║ s[0] ║ 31*h_old + s[1] = ║
║ ║ ║ 31 *s[0] + s[1] ║
║ ║ ║ ║
║ 3 ║ 31 *s[0] + s[1] ║ 31^2 *s[0] + 31 *s[1] + s[2] ║
║ ║ ║ ║
║ 4 ║ 31^2*s[0] + 31*s[1] + s[2] ║ 31^3 *s[0] + 31^2 *s[1] + 31*s[2] + s[3] ║
║ : ║ : ║ : ║
║ n ║ ... ║ 31^(n-1)*s[0] + 31^(n-2)*s[1] + ... + 31^0*s[n] ║
╚═════╩════════════════════════════╩═════════════════════════════════════════════════╝
(Table 用 Senseful 生成)
31
的幂是通过循环和 h
与 31
的常数乘法(利用乘法的 distributivity)创建的。
正如我们在 table 的最后一行中看到的那样,这正是文档所说的。
下面是 String.hashCode()
方法的源代码片段,来自 Java 8(准确地说是 1.8.0_131)
/**
* 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;
}
您可以看到,文档说,hashCode()
是使用以下公式计算的
s[0]*31^(n-1) + s[1]*31^(n-2) + ... + s[n-1]
虽然实际实现不同
for (int i = 0; i < value.length; i++) {
h = 31 * h + val[i];
}
我漏掉了什么明显的东西吗?请帮助我。
实施是正确的,需要注意的是可能会发生整数溢出(在这里没关系,它不会造成任何损害)。它使用 Horner's method 进行多项式计算。
下面是示例字符串的步骤 "CAT"。
h = 0
第一个循环:
i = 0
h = 31 * 0 + 'C' (67) = 67
第二个循环:
i = 1
h = 31 * 67 + 'A' (65) = 2142
第三个循环:
i = 2
h = 31 * 2142 + 'T' (84) = 66486
让我们从代码中推导出公式。这里,n 是 i
在字符串 s 中的索引。 for
循环的每次迭代都执行此公式。
hn = 31hn-1 + sn
h0 /* after loop i = 0 */ = s[0]
h1 /* after loop i = 1 */ = 31*h0 + s[1] = 31*s[0] + s[1]
h2 /* after loop i = 2 */ = 31*h1 + s[2] = 31*(31*s[0] + s[1]) + s[2]
h = 31*31*s[0] + 31*s[1] + s[2]
您看到的 31 次方的指数是因为每个循环在添加下一个字符的值之前乘以另一个 31
的因数。
通过一些例子最容易看出会发生什么。让我们采用长度为 n
的字符串 s
和上述所有符号。我们将分析迭代的循环迭代。我们将 h_old
称为 h
在当前迭代开始时的值,h_new
表示 h
在当前迭代结束时的值。很容易看出迭代 i
的 h_new
将是迭代 i + 1
.
h_old
╔═════╦════════════════════════════╦═════════════════════════════════════════════════╗
║ It. ║ h_old ║ h_new ║
╠═════╬════════════════════════════╬═════════════════════════════════════════════════╣
║ 1 ║ 0 ║ 31*h_old + s[0] = ║
║ ║ ║ s[0] ║
║ ║ ║ ║
║ 2 ║ s[0] ║ 31*h_old + s[1] = ║
║ ║ ║ 31 *s[0] + s[1] ║
║ ║ ║ ║
║ 3 ║ 31 *s[0] + s[1] ║ 31^2 *s[0] + 31 *s[1] + s[2] ║
║ ║ ║ ║
║ 4 ║ 31^2*s[0] + 31*s[1] + s[2] ║ 31^3 *s[0] + 31^2 *s[1] + 31*s[2] + s[3] ║
║ : ║ : ║ : ║
║ n ║ ... ║ 31^(n-1)*s[0] + 31^(n-2)*s[1] + ... + 31^0*s[n] ║
╚═════╩════════════════════════════╩═════════════════════════════════════════════════╝
(Table 用 Senseful 生成)
31
的幂是通过循环和 h
与 31
的常数乘法(利用乘法的 distributivity)创建的。
正如我们在 table 的最后一行中看到的那样,这正是文档所说的。