兄弟们,有没有这种经历?
面试官一上来就问:“说说HashMap吧。” 你心里一咯噔,赶紧背:“基于哈希表,K-V结构,线程不安全……” 然后面试官点点头,又问:“那它怎么解决哈希冲突的?” 你:“链表+红黑树。” 面试官:“那什么时候转红黑树?” 你:“8个节点。” 面试官:“扩容机制呢?” 你:“2倍扩容……” 说着说着,自己都快睡着了,面试官也一脸“这人背得挺熟”的表情。
但你有没有发现——你根本不知道它为啥这么设计?
下面记录一下
1. HashMap是干啥的?
想象一下,你手机里存了1000个联系人。 你想找“王建国”,你是从头一个一个翻,还是直接搜“王建国”? 当然是搜啊! HashMap就是干这个的——用Key快速找到Value。
比如:
Map<String, String> phoneBook = new HashMap<>();
phoneBook.put("王建国", "13888888888");
phoneBook.get("王建国"); // 直接拿到电话
它不像List那样得遍历,HashMap是O(1),一步到位。
2. 为啥会有哈希冲突?
你说,我用名字当Key,那“王建国”多了去了,全国多少个? HashMap也怕撞名。 它怎么处理? 它先算“王建国”的哈希值,比如是 105。 然后它说:“好,你存到第105个位置去。” 但问题来了,李建国的哈希值也可能是105。 这不就撞上了? 这叫哈希冲突。 那HashMap说:“行,你不就是挤吗?那你们就链表排队。” 一个位置,挂一串人,像地铁早高峰。 但问题又来了。 如果这个位置排队的太多,比如10个人,那你找“王建国”就得一个一个问,效率就低了。 所以,JDK1.8之后,一旦链表长度超过8,就升级成红黑树。
红黑树是啥? 可以理解成——从排队升级成二分查找,效率立马从O(n)干到O(log n)。 这就像地铁口从混乱插队,变成了扫码分批入场。
3. 扩容?
HashMap不是一开始就很大。 它默认初始容量是16,负载因子0.75。
啥意思? 就是说,最多存12个(16*0.75),第13个进来,就得扩容。 扩容就是——空间翻倍,变成32。 然后所有元素重新计算位置,搬一次家。 这叫rehash。 听起来很贵?确实。 所以,如果你知道要存1000个数据,最好一开始就指定容量:
Map<String, String> map = new HashMap<>(1024);
不然它得搬好几次家,累死。 这就像你租房,一开始住小单间,结果朋友越来越多,三个月搬五次,谁受得了? 提前租个大房子,才省心。
4. 实际案例
我们组之前搞个活动系统,要处理20万用户抽奖。 逻辑很简单:同一个用户,只能中一次奖。
程序员小李这么写:
Map<String, Prize> userPrizeMap = new HashMap<>();
for (UserOrder order : orderList) {
String userId = order.getUserId(); // 用户ID,比如 "1001"
if (!userPrizeMap.containsKey(userId)) {
userPrizeMap.put(userId, order.getPrize());
}
}
看着是没问题? 结果一上线,CPU直接飙到90%,接口超时,活动差点崩了。 查了半天,发现不是数据量大,而是他用的是 containsKey + put,等于查了两遍! containsKey 查一次哈希表,put 又查一次(看是否已存在),等于每个用户都double打卡,做了两次查找。 20万用户,就是40万次查找,白烧CPU。 后来改成一行代码:
// 利用 put 的返回值判断是否已存在
if (userPrizeMap.put(userId, order.getPrize()) == null) {
// 说明是新增的,之前没有
sendPrize(order);
}
put本身就会返回旧值:
如果是null,说明之前没存过,是新用户;
如果有值,说明重复了,自动覆盖。
一招搞定,查找只做一次。
结果:CPU从90%干到30%,处理时间从8秒降到2秒。
6. 面试装X指南
下次面试官再问HashMap,别背了。
你可以这么说:
想象你去食堂打饭。 你想吃红烧肉,师傅一看菜牌,直接端给你。 但今天人多,好几个人都要红烧肉。 怎么办? 排一队,一个一个打。(这就是链表) 结果队伍太长,后面的人都饿哭了。 食堂升级了: 要红烧肉的人,直接去3号窗口智能取餐机扫码拿!(这就是转成红黑树,更快) 而且,食堂发现菜不够了,立马加锅加灶,扩大档口。(这就是扩容)
最关键: 师傅怎么知道谁该排哪队? 他不是光看“要啥菜”,还会看你的饭卡号搅一搅,避免全挤一个窗口。 这就是 HashMap。 存得快,取得快,人多也不乱。 你看,有场景,有比喻,有细节。 面试官心里OS:“这人……好像真懂。”
结尾叨叨
HashMap不是用来背的,是用来理解的。 你懂它为啥扩容,为啥转红黑树。 你就能在写代码时,避开坑,写出更稳的程序。
最后灵魂一问:
你项目里有没有因为HashMap用错,导致性能翻车的?
评论区聊聊,让我也乐呵乐呵。
作者大华,关注我,别让代码耽误你下班! 觉得有用的话,点个爱心吧!
📌往期精彩
《Elasticsearch 太重?来看看这个轻量级的替代品 Manticore Search》 《别再if套if了!Java中return的9种优雅写法》 《别学23种了!Java项目中最常用的6个设计模式,附案例》 《Vue3+TS设计模式:5个真实场景让你代码更优雅》