说是面了个28岁的候选人,某大厂T5,跳槽张口就是65k。擦,哥们你是来面试的,还是来给我上强度的?
结果一看简历,人家还真不是乱喊价。985硕士,4年经验,做过核心项目,数据也好看,面试聊下来,逻辑在线,技术不飘,对业务还真懂。说白了,这种人放市场上,手慢点都抢不着。
网友们的评论也很真实。有人说,别看到28岁就震惊,现在不是按年龄算钱,是按“能不能立刻干活还少废话”算钱。
我看完也有点沉默。不是人家要高了,是很多打工人一直拿“年龄到了”“资历够了”安慰自己,结果市场早就不认这套。
说难听点,公司嘴上说看潜力,发钱的时候只看你能不能马上顶上去。年龄这玩意,在工资条面前,真没那么值钱。
面试题:日志速率限制器
这题叫“日志速率限制器”,名字不复杂,真正容易写乱的地方在于:限制的是同一条消息,不是整个系统的总日志量。
题目常见表述是这样:同样的日志内容,10 秒内只允许打印一次。比如 foo 在 1 秒打印了,2 秒再来一次就得拦住,到了 11 秒才能重新放行。
这个题第一眼看上去像队列,其实顺手写的话,用 HashMap 就够了。核心就是记住一条消息上一次被放行的时间。
我自己一般会先写成这样:
classLogger{privatefinal Map<String, Integer> lastPrintTime = new HashMap<>();publicbooleanshouldPrintMessage(int timestamp, String message){ Integer lastTime = lastPrintTime.get(message);if (lastTime == null) { lastPrintTime.put(message, timestamp);returntrue; }if (timestamp - lastTime >= 10) { lastPrintTime.put(message, timestamp);returntrue; }returnfalse; }}
这段代码不花,逻辑也直:
拿一组输入过一下更直观:
Logger logger = new Logger();System.out.println(logger.shouldPrintMessage(1, "order timeout")); // trueSystem.out.println(logger.shouldPrintMessage(2, "order timeout")); // falseSystem.out.println(logger.shouldPrintMessage(11, "order timeout")); // trueSystem.out.println(logger.shouldPrintMessage(12, "db slow")); // trueSystem.out.println(logger.shouldPrintMessage(15, "db slow")); // false
这题的关键不在代码长短,而在边界判断。
比如这里为什么是 >= 10,不是 > 10?因为题目说的是“最近 10 秒内不能重复打印”,那到了第 10 秒边界,已经可以重新打印了。这个地方写错,结果会差一位。
再往下想一步,这个写法时间复杂度是 O(1),查一次 Map,更新一次 Map,面试里是够用的。空间复杂度取决于 message 的种类数,最坏就是 O(n)。
不过如果你真把它当线上组件,还会有一个现实问题:HashMap 会一直涨。因为老消息虽然以后再也不用了,但还留在内存里。线上做法一般会补一个清理策略,或者直接配合队列把过期数据淘掉。
可以简单补个思路:
classNode{ String msg;int time;}
每次放行成功时,把消息和时间顺手塞进队列;后续新请求进来时,先把超过 10 秒窗口的数据从队列头部清掉,再同步删掉 Map 里的旧记录。这样就更像一个真实的“限流窗口”了。
所以这题本身不难,难的是别把它写成“日志系统设计题”。题目只要求判断某条消息当前能不能打印,抓住“消息 -> 最近一次打印时间”这个映射,基本就不会偏。面试官一般也不是想看你堆多少花活,而是看你能不能把规则压成一段稳定、可解释、边界清楚的代码。