# 有理有据！为什么String选择数字31作为hashCode方法乘子?

### 2. 选择数字31的原因

public int hashCode() {
int h = hash;
if (h == 0 &amp;&amp; value.length &gt; 0) {
char val[] = value;

for (int i = 0; i &lt; value.length; i++) {
h = 31 * h + val[i];
}
hash = h;
}
return h;
}


s[0]*31^(n-1) + s[1]*31^(n-2) + ... + s[n-1]


假设 n=3
i=0 -&gt; h = 31 * 0 + val[0]
i=1 -&gt; h = 31 * (31 * 0 + val[0]) + val[1]
i=2 -&gt; h = 31 * (31 * (31 * 0 + val[0]) + val[1]) + val[2]
h = 31*31*31*0 + 31*31*val[0] + 31*val[1] + val[2]
h = 31^(n-1)*val[0] + 31^(n-2)*val[1] + val[2]


The value 31 was chosen because it is an odd prime. If it were even and the multiplication overflowed, information would be lost, as multiplication by 2 is equivalent to shifting. The advantage of using a prime is less clear, but it is traditional. A nice property of 31 is that the multiplication can be replaced by a shift and a subtraction for better performance: 31 * i == (i << 5) - i. Modern VMs do this sort of optimization automatically.

As Goodrich and Tamassia point out, If you take over 50,000 English words (formed as the union of the word lists provided in two variants of Unix), using the constants 31, 33, 37, 39, and 41 will produce less than 7 collisions in each case. Knowing this, it should come as no surprise that many Java implementations choose one of these constants.

### 3. 实验及数据可视化

3.1 哈希值冲突率计算

public static Integer hashCode(String str, Integer multiplier) {
int hash = 0;
for (int i = 0; i &lt; str.length(); i++) {
hash = multiplier * hash + str.charAt(i);
}

return hash;
}

/**
* 计算 hash code 冲突率，顺便分析一下 hash code 最大值和最小值，并输出
* @param multiplier
* @param hashs
*/
public static void calculateConflictRate(Integer multiplier, List&lt;Integer&gt; hashs) {
Comparator&lt;Integer&gt; cp = (x, y) -&gt; x &gt; y ? 1 : (x &lt; y ? -1 : 0);
int maxHash = hashs.stream().max(cp).get();
int minHash = hashs.stream().min(cp).get();

// 计算冲突数及冲突率
int uniqueHashNum = (int) hashs.stream().distinct().count();
int conflictNum = hashs.size() - uniqueHashNum;
double conflictRate = (conflictNum * 1.0) / hashs.size();

System.out.println(String.format("multiplier=%4d, minHash=%11d, maxHash=%10d, conflictNum=%6d, conflictRate=%.4f%%",
multiplier, minHash, maxHash, conflictNum, conflictRate * 100));
}


3.2 哈希值分布可视化

/**
* 将整个哈希空间等分成64份，统计每个空间内的哈希值数量
* @param hashs
*/
public static Map&lt;Integer, Integer&gt; partition(List&lt;Integer&gt; hashs) {
// step = 2^32 / 64 = 2^26
final int step = 67108864;
List&lt;Integer&gt; nums = new ArrayList&lt;&gt;();
Map&lt;Integer, Integer&gt; statistics = new LinkedHashMap&lt;&gt;();
int start = 0;
for (long i = Integer.MIN_VALUE; i &lt;= Integer.MAX_VALUE; i += step) {
final long min = i;
final long max = min + step;
int num = (int) hashs.parallelStream()
.filter(x -&gt; x &gt;= min &amp;&amp; x &lt; max).count();

statistics.put(start++, num);
}

// 为了防止计算出错，这里验证一下
int hashNum = nums.stream().reduce((x, y) -&gt; x + y).get();
assert hashNum == hashs.size();

return statistics;
}


3作为乘子时，算出的哈希值分布情况和2很像，只不过稍微好了那么一点点。从图中可以看出绝大部分的哈希值最终都落在了第32分区里，哈希值的分布性很差。这个也没啥用，拖出去枪毙5分钟吧。在看看数字17的情况怎么样：

11-19 3245

09-29 1万+
12-20 1万+
09-07 5478
05-24 6万+
11-14 6713
12-23 2415
06-07 5702
08-05 1万+
09-03 2518
03-06 108
05-27 1876