新来的香港leader review我代码,跟我说注释能删就删掉。。
这香港 leader 还真有点狠。
新同事被 review 代码,对方上来不是挑命名,也不是挑性能,直接说:注释能少就少,能删就删。理由也挺扎心,代码改来改去,最后最容易被遗忘的就是注释。代码已经变了,注释还停在上个版本,后面的人一看,反而被带沟里。
听着有点反常识,但细想还真不是没道理。
很多人写注释,其实不是解释复杂逻辑,是给烂代码擦屁股。变量名起得像谜语,函数拆得像一坨,最后靠注释硬解释:这里是干嘛,那里为啥这么写。结果过两个月需求一改,注释没人管,锅还更大。
好的代码确实应该自己会说话。命名清楚,结构干净,逻辑顺一点,别人扫两眼就知道你想干嘛。注释不是不能写,但别拿它当遮羞布。
这题第一眼别急着写 SQL,也别急着套什么窗口函数。 “累计薪水”这四个字很容易把人带偏,真要看清楚题意:每个员工按月份算最近 3 个月的工资和,而且员工最新月份不参与输出。
比如员工 1 有这些工资:
id = 1
month: 1 2 3 4
salary:20 30 40 60
输出的时候,month=4 是这个员工最新月份,要跳过。
剩下:
month=3 -> 1、2、3 月工资和 = 90
month=2 -> 1、2 月工资和 = 50
month=1 -> 1 月工资和 = 20
这地方我一般不会先想复杂数据结构。题目给的是“员工 + 月份 + 薪水”,天然就是两层关系:先按员工分组,再按月份排序。
Java 写的话,用 Map<Integer, TreeMap<Integer, Integer>> 就够了。
Map 管员工,TreeMap 管月份。 月份有序之后,再倒着扫,每个月往前最多找 3 个月。
代码可以这样写:
import java.util.*;
publicclassSalaryQuery{
staticclassPay{
int id;
int month;
int salary;
Pay(int id, int month, int salary) {
this.id = id;
this.month = month;
this.salary = salary;
}
}
staticclassRow{
int id;
int month;
int total;
Row(int id, int month, int total) {
this.id = id;
this.month = month;
this.total = total;
}
@Override
public String toString(){
return id + " " + month + " " + total;
}
}
publicstatic List<Row> query(List<Pay> pays){
Map<Integer, TreeMap<Integer, Integer>> salaryBook = new HashMap<>();
for (Pay pay : pays) {
salaryBook
.computeIfAbsent(pay.id, k -> new TreeMap<>())
.put(pay.month, pay.salary);
}
List<Row> ans = new ArrayList<>();
for (Map.Entry<Integer, TreeMap<Integer, Integer>> entry : salaryBook.entrySet()) {
int empId = entry.getKey();
TreeMap<Integer, Integer> months = entry.getValue();
int newestMonth = months.lastKey();
for (Integer month : months.descendingKeySet()) {
if (month == newestMonth) {
continue;
}
int sum = 0;
for (int m = month; m >= month - 2; m--) {
sum += months.getOrDefault(m, 0);
}
ans.add(new Row(empId, month, sum));
}
}
ans.sort((a, b) -> {
if (a.id != b.id) {
return b.id - a.id;
}
return b.month - a.month;
});
return ans;
}
publicstaticvoidmain(String[] args){
List<Pay> pays = Arrays.asList(
new Pay(1, 1, 20),
new Pay(1, 2, 30),
new Pay(1, 3, 40),
new Pay(1, 4, 60),
new Pay(2, 1, 15),
new Pay(2, 2, 25)
);
List<Row> rows = query(pays);
for (Row row : rows) {
System.out.println(row);
}
}
}
这段代码里有两个点别漏。
第一个,每个员工最新月份不能输出。不是全表最新月份,是每个员工自己的最新月份。这个地方写错,结果基本就歪了。
第二个,累计薪水不是从入职开始一直加到当前月,而是最多看最近 3 个月:
for (int m = month; m >= month - 2; m--)
没有这个月份,就当 0 处理:
months.getOrDefault(m, 0)
有些人会先把所有月份补齐,其实没必要。题目要的是已有工资记录对应月份的结果,不是生成一张完整月份日历。
最后排序也按题目常见要求来:员工 id 倒序,月份倒序。 这类题不难,坑主要不在代码量,而在题意:跳过最新月、只累最近三个月、按员工独立计算。这三个条件卡住,代码就不会乱。