Java字符串hashCode实现疑问:短串匹配长串结果不符?
问题分析:自定义hashCode与原生实现的差异原因
你的实现和原生String.hashCode()在长字符串时结果不同,核心原因是浮点数精度丢失和整数溢出的处理方式不一致,属于实现有误,不是正常情况。
先明确原生String.hashCode()的计算逻辑
Java官方对String.hashCode()的定义是:
s[0]*31^(n-1) + s[1]*31^(n-2) + ... + s[n-1]
其中使用int整数算术进行计算,溢出时直接按照int的有符号补码规则处理(即自动对2^32取模)。
但原生实现为了避免直接计算大指数(效率低且易出问题),实际是用迭代方式实现的,等价于:
public int hashCode() { int h = 0; byte[] val = value; // String内部的字符数组 int len = length; for (int i = 0; i < len; i++) { h = 31 * h + val[i]; } return h; }
你的实现存在的两个关键问题
- 浮点数精度丢失:你使用
Math.pow(31, power)计算幂次,Math.pow返回的是double类型。当power较大时(比如超过15左右),31的幂次会超出double能精确表示的整数范围,转成int时会得到错误的数值。 - 溢出处理不一致:原生实现是通过迭代逐步累积,每次计算
31 * h + val[i]时的溢出是符合int类型规则的;而你的实现先计算每个项的大数值(再转int),这个过程的溢出逻辑和原生完全不同,最终结果自然无法匹配。
修复后的正确实现
要和原生hashCode()结果完全一致,应该采用迭代式的整数计算,避免浮点数操作:
public int hash(String str) { int hashValue = 0; for (int i = 0; i < str.length(); i++) { hashValue = 31 * hashValue + str.charAt(i); } return hashValue; }
这个实现和原生逻辑完全等价,无论字符串长短,结果都会和str.hashCode()一致。
内容的提问来源于stack exchange,提问作者Amit Nachimovitz
相关产品推荐
相关产品推荐

