Java String.hashCode()的哈希值是通过以31为底的加权和计算的确定性整数,公式为s[0]×31^(n−1)+…+s[n−1]×31^0,实际采用霍纳法迭代h=31×h+char实现,31因是奇质数且支持位运算优化(31×h等价于(h<<5)−h)而被选用。
![java中 string 字符串哈希值计算公式 s[0]*31^(n-1) 怎么理解](https://img.php.cn/upload/article/001/242/473/178428213769265.jpeg)
这个公式是 Java String.hashCode() 的数学表达形式,本质是把字符串看作一个以 31 为底的“数字”,每个字符是某一位上的“数字”,位置越靠前权重越高。
它不是真算幂,而是霍纳展开
公式写成 s[0]×31^(n−1) + s[1]×31^(n−2) + … + s[n−1]×31^0 看似要算很多次方,但实际代码里从不这么算。JVM 用的是等价的迭代方式:
- 从左到右遍历每个字符
- 每步执行
h = 31 * h + currentChar - 初始
h = 0
比如 "ab"('a'=97, 'b'=98):
第一步:h = 31×0 + 97 = 97
第二步:h = 31×97 + 98 = 3105
这和直接算 97×31¹ + 98×31⁰ = 3105 完全一致——只是避免了大指数和重复计算。
为什么首字符乘的是 31^(n−1)
因为字符串越长,前面字符的影响被放大得越多。这带来两个实际效果:
- 前缀相同的字符串(如
"abc"和"abcd")哈希值通常接近,利于 HashMap 中桶的局部性 - 微小改动(比如改第一个字符)会显著改变结果,降低冲突概率
反过来看,末尾字符只乘 1,影响最小——这也是为什么两个互为反转的字符串(如 "gdejicbegh" 和 "hgebcijedg")偶尔会撞出相同 hash 值。
立即学习“Java免费学习笔记(深入)”;
31 不是 magic number,而是权衡选择
选 31 主要出于三点考虑:
- 它是奇质数,能减少因字符组合相似导致的哈希重复
-
31 * h可优化为(h ,位运算比乘法快 - 数值适中:太大容易溢出失真,太小又离散性差;实测在常见字符串分布下,31 比 29、37 等略优
所有运算都在 int 范围内进行,溢出不报错,而是自然回绕——这反而保证了结果确定性。
空串和单字符是公式的自然结果
公式本身已涵盖边界情况:
- 空串
"":长度 n=0,求和项为空 → hash=0 - 单字符如
"x":n=1,只有s[0]×31⁰ = s[0]→ hash 就是字符的 Unicode 值
不需要额外判断,代码里循环不执行就直接返回初始值 0,完全契合数学定义。


















