Android 上 String.hashCode 的实现稳定吗?

今天在排查反馈问题的时候,注意到有一处逻辑将几个属性拼接成 String 再计算 hashCode 作为表的主键使用。显然这是一处对于 DB 数据索引的小优化( 对比传统复合主键写法来说 ),但理论上可能存在两个问题:

  • 数据项间存在主键计算结果哈希冲突的情况,会导致 DB 数据错乱
  • String 的 hashCode 实现跨 ART 实例 / 系统版本不稳定,会导致 DB 数据无法被正确索引

具体实践上来说,在项目主工程中全局搜索了下,发现有多处类似的写法( 大概是出现了历史代码人传人的情况 ),最早的提交记录可以追溯到 15 年下半年,且某些表重度用户应该会出现上万条表记录的情况,基于此可以认为截止到当下,上述两个问题都不太可能会出现。

哈希冲突的问题,理论上分布均匀的 hashCode 实现,大约超过 70k 条数据就会有 50% 的概率出现,详细讨论可参见 String.hashCode() is plenty unique

至于 String.hashCode 实现的稳定性,是否有一个明确的约定呢?

从系统版本跨 ART 实例来说( 即同一 ART 实现跨进程的情况 ),String.hashCode 的实现显然是稳定的。

从 String.hashCode 的文档上也可以看出来: 

 注意到文档上甚至直接规定了 String.hashCode 的算法,实际也就是 Android Studio 默认帮我们补全 hashCode 实现的那一种。

从跨 Android 系统版本来说,String.hashCode 的实现是否是保证稳定的呢?

前面提到 String 的接口文档上是直接规定了 hashCode 的算法的,但接口文档本身理论上也可以随着版本的变更而变更,是否有这么一个地方明确定义了 String.hashCode 的跨版本兼容性呢?

于是我问了下 chatGPT,它的答案是 Java 语言规范中明确规定了 String 的 hashCode 算法,给出的引用是这样的: 

 似乎很有道理,我顺着链接 jls-17.5.3 找了下 JLS 的内容,发现引用的章节实际既不是讲 String 也不是讲 hashCode 的,没关系,前后几个版本的 JLS 也全局搜索下 hash,结果也没有相关内容,果然 chatGPT 又是在一本正经地逗我...

只能自力更生 Google 了,翻到了一个上古年代 JDK 的 bug 记录 java.lang.String.hashCode spec incorrectly describes the hash algorithm,里面提到了最早的 JLS 上确实描述了 String.hashCode 的算法,但实现却和规范不符,这个问题在 JDK 1.2 上被修复,同时 JLS 的描述应该被相应地更新( 看来 chatGPT 虽然骗了我,但又没有完全骗 )。不过 SE1.2 对应的 JLS 文档简单找了下没找到,而 SE6 对应的 JLS 文档上就确实没有 String.hashCode 的相关描述了。这里可以看到至少在上古年代的 JDK 实现上,String.hashCode 确实是存在有实现不一致的情况的,不过这跟 Android 是没啥关系了。

发现 Stack Overflow 很多年前也就有过类似的提问: Consistency of hashCode() on a Java string,从讨论内容看,普遍的共识是从向后兼容考虑,大概率 JDK 的实现也不会修改这个长久以来都很稳定的 String.hashCode 算法了。

Android 上 String.hashCode 的实现稳定吗?大概它是稳定的。不过对于类似需要计算 hashCode 再持久化存储的场景,我选择直接让 IDE 帮我生成一个绝对稳定的 hashCode 实现 🐶。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值