程序员老鬼

新来的香港leader review我代码,跟我说注释能删就删掉。。

这香港 leader 还真有点狠。

新同事被 review 代码,对方上来不是挑命名,也不是挑性能,直接说:注释能少就少,能删就删。理由也挺扎心,代码改来改去,最后最容易被遗忘的就是注释。代码已经变了,注释还停在上个版本,后面的人一看,反而被带沟里。

Image

听着有点反常识,但细想还真不是没道理。

很多人写注释,其实不是解释复杂逻辑,是给烂代码擦屁股。变量名起得像谜语,函数拆得像一坨,最后靠注释硬解释:这里是干嘛,那里为啥这么写。结果过两个月需求一改,注释没人管,锅还更大。

好的代码确实应该自己会说话。命名清楚,结构干净,逻辑顺一点,别人扫两眼就知道你想干嘛。注释不是不能写,但别拿它当遮羞布。

今日面试题

这题第一眼别急着写 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 倒序,月份倒序。 这类题不难,坑主要不在代码量,而在题意:跳过最新月、只累最近三个月、按员工独立计算。这三个条件卡住,代码就不会乱。