为了账号安全,请及时绑定邮箱和手机立即绑定

哈希冲突那些事:一场 Key 的偶遇

标签:
Java JavaScript

原文来自于:https://zha-ge.cn/java/49

哈希冲突那些事:一场 Key 的偶遇

有一次深夜撸代码,心里怀着三分无奈七分好奇地改着一个看上去无比普通的 HashMap。你知道嘛,程序员的第六感有时候很灵:我总觉得,这代码,哪里总藏着点“不安分”的小虫子。

咖啡还没喝尽,堆栈溢出给了点提示:Hash 碰撞(hash collision)!听着像是街头偶遇帅哥,结果两人都戴口罩,认错了还掐了一架。其实,咱们用得再熟的 Map,底下也总有点不可控的小尴尬。

底层那些花里胡哨的小九九

  • 所谓“哈希”,其实就是给每个 key 分配个“数字身份证”。
  • 但你见过身份证号真能全唯一?当然不行(毕竟数据类型有限嘛)!
  • 所以如果 “张三” 和 “李四” 恰好拿到了同一个 hash 值,他们两个存入 map 时,那就只好“同屋共居”了。

来,给你看看经典的“同屋共居”场景(Java 的哈希桶):

int hash = key.hashCode();
int bucketIndex = hash % table.length;
Node<K,V> node = table[bucketIndex];
if(node != null && node.key.equals(key)) {
    // 已存在,更新值
    node.value = value;
} else {
    // 不存在,新插入一个链表节点
    // ...
}

一言以蔽之:hash 相同了?没事,咱们链表里挤一挤!再不行,直接换成红黑树了都行。优雅……嗯,算是一种妥协吧。

踩坑瞬间

最让人蛋疼的是,这碰撞一旦多起来,性能可不是线性衰减的:

  • put 10万条数据,结果慢得像蜗牛——查出来,全撞一个桶里了,一条链表挤爆了。
  • 想当然地自定义了个 hashCode 方法,结果所有 key 的 hashCode 都等于 1?恭喜你,HashMap 正式退化成了 LinkedListMap!
  • 误用对象做 key,却没重写 equals/hashCode,分分钟背锅——查,说好的唯一咋变成“同名同姓”了?

我记得那次真的调到想哭。生产上突然日志飘红——“耗时 xxx ms 超标”,泪奔,一看,哈希全撞了!

解决姿势&土味金句集

秉承“有坑必填”的信条,总结点救命要诀:

  1. 写 key 的 hashCode/equals 时别偷懒
    • 千万别全返回一样……要有分散性!
  2. 插数据多就记得用容量大点的桶
    • 初始容量设小了,表分分钟变热门小区。
  3. 老版本 JDK 的 HashMap 要注意线程安全
    • 多线程环形链表乱舞,直接进 ICU。

还可以这样画个脑图:

异常情况 应对措施
hash 过于集中 优化 hashCode
桶容量不足 增大负载因子 or 指定初始容量
key 不唯一 实现合理的 equals

经验启示

几个不想再重犯的错误,忍痛记在小本本上:

  • 再小的工具,都藏着“街头偶遇”的风险:Key 选得不好,Hash 就会撞得头破血流。
  • 世界没有完美 hash,只有充分测试。咱自己造的 hash,也得担得起锅。
  • 性能问题,80% 都是数据结构选错或用错——要用而不用、滥用成推土机都不行。
  • 混合着踩坑、吐槽和小感慨,才是一个 Javaer 最真实的成长轨迹……

说到这,再晚也要收个尾巴。写代码,多想几步,多测几步。哪怕是 HashMap 这种“熟人”,它也会在不经意间给你上演一出“缘分的天空”。兄弟,咖啡续起来,下次咱们再聊聊 ConcurrentHashMap 吧~

—— 码到深夜的 HashMap 徒

点击查看更多内容
TA 点赞

若觉得本文不错,就分享一下吧!

评论

作者其他优质文章

正在加载中
  • 推荐
  • 评论
  • 收藏
  • 共同学习,写下你的评论
感谢您的支持,我会继续努力的~
扫码打赏,你说多少就多少
赞赏金额会直接到老师账户
支付方式
打开微信扫一扫,即可进行扫码打赏哦
今天注册有机会得

100积分直接送

付费专栏免费学

大额优惠券免费领

立即参与 放弃机会
微信客服

购课补贴
联系客服咨询优惠详情

帮助反馈 APP下载

慕课网APP
您的移动学习伙伴

公众号

扫描二维码
关注慕课网微信公众号

举报

0/150
提交
取消