某字节员工直言:做程序员久了,发现大家都有个共同之处,就是家庭条件真的都不太好,不绝对,但是不少人都是这种情况
刚刷到个贴子,说字节一位程序员感慨:做这行久了,发现身边同事家庭条件普遍一般,没啥富二代。
挺现实的,编程这种工作,本质是靠时间和脑力去换钱,门槛靠学习堆上去,不像一些资源型行业,家里要有背景才能起步。换句话说,家境普通的人,更愿意也更有动力通过技术改变命运。
从大环境看,程序员算是“努力能见效”的典型行业,所以吸引了大批出身普通、想靠自己翻盘的人。这一点挺值得尊敬。【备注:文末可领最新资料】
算法题:2020年最后一次登录
昨天晚上十一点多,在公司楼下便利店等热关东煮,手机又跳出一道面试题,脑子一激灵:哎,就是那个…“2020年最后一次登录”。你们也碰到过对吧?看着简单,坑可不少,先把场景说人话:给一堆登录日志,每条像userId, loginTime,时间可能乱序、可能带时区、同一天多次也可能。要的是——每个用户在“2020 年”里的“最后一次”登录。超过 2020 就别算,没登过的也别输出。嗯,就这么个意思。
说下思路哈,我当时一边喝豆乳一边想:别整花活,线性走一遍就够。核心就是三个动作:先“过滤”出 2020 年的,接着“比较”同一用户最大的时间点,最后“输出”每个用户的那条。注意“2020 年”的判定要按业务时区来,不然 UTC+0 和 GMT+8 一天能差八小时,跨年边界会飘。我们组那个小李就被这个坑过一次…算了不黑他了。
还有几个细节,别忽略: 1)时间乱序无所谓,我们是在线更新最大值; 2)同一时间多条,按精确到毫秒比较即可,真撞了就保留其一; 3)日志里有各种格式?最好约定 ISO-8601(2020-12-31T23:59:59+08:00),否则就给多套 DateTimeFormatter 逐个尝试; 4)内存别怕,Map 一把梭;超大数据就分片/流式聚合。
我把代码贴下,用 Java 8+ 的 ZonedDateTime,默认按业务时区(举例 Asia/Shanghai),如果传入的字符串自带偏移量,会以字符串为准,再转到业务时区判断是不是 2020。这样跨时区的边界也稳了。
import java.time.*;
import java.time.format.DateTimeFormatter;
import java.time.format.DateTimeParseException;
import java.util.*;
publicclassLastLogin2020{
// 业务时区,可配置
privatestaticfinal ZoneId BIZ_ZONE = ZoneId.of("Asia/Shanghai");
// 常见格式:ISO-8601优先,其次无偏移的本地时间
privatestaticfinal List<DateTimeFormatter> Fmts = Arrays.asList(
DateTimeFormatter.ISO_ZONED_DATE_TIME, // 2020-12-31T23:59:59+08:00
DateTimeFormatter.ISO_OFFSET_DATE_TIME, // 2020-12-31T23:59:59+08:00
DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss"), // 2020-12-31 23:59:59(按业务时区解释)
DateTimeFormatter.ISO_LOCAL_DATE_TIME // 2020-12-31T23:59:59
);
publicstaticclassLog{
publicfinal String userId;
publicfinal String timeStr;
publicLog(String u, String t){ userId = u; timeStr = t; }
}
publicstatic Map<String, ZonedDateTime> lastLoginIn2020(List<Log> logs){
Map<String, ZonedDateTime> ans = new HashMap<>();
for (Log log : logs) {
ZonedDateTime zdt = parseToBizZone(log.timeStr);
if (zdt == null) continue;
if (zdt.getYear() != 2020) continue; // 只算2020
ZonedDateTime old = ans.get(log.userId);
if (old == null || zdt.isAfter(old)) {
ans.put(log.userId, zdt);
}
}
return ans;
}
privatestatic ZonedDateTime parseToBizZone(String s){
for (DateTimeFormatter f : Fmts) {
try {
if (f == DateTimeFormatter.ISO_ZONED_DATE_TIME ||
f == DateTimeFormatter.ISO_OFFSET_DATE_TIME) {
// 自带偏移,尊重它,再转业务时区用于“按年判断”
return ZonedDateTime.parse(s, f).withZoneSameInstant(BIZ_ZONE);
} else {
// 无偏移,当作业务时区的本地时间
LocalDateTime ldt = LocalDateTime.parse(s, f);
return ldt.atZone(BIZ_ZONE);
}
} catch (DateTimeParseException ignore) { }
}
returnnull; // 实在解析不了就丢掉
}
// ---- demo ----
publicstaticvoidmain(String[] args){
List<Log> logs = Arrays.asList(
new Log("A", "2019-12-31T23:59:59+08:00"),
new Log("A", "2020-12-31T23:59:59+08:00"),
new Log("A", "2020-12-31 23:59:58"),
new Log("B", "2021-01-01T00:00:00+09:00"), // 换到上海可能仍是2020-12-31 23:00
new Log("B", "2020-01-01T00:00:01+08:00"),
new Log("C", "oops") // 脏数据
);
Map<String, ZonedDateTime> res = lastLoginIn2020(logs);
res.forEach((u,t) -> System.out.println(u + " -> " + t));
}
}
复杂度这块儿别纠结,单次扫描 O(n),空间 O(u),u 是用户数。要是数据特别大…有人问我那个配置怎么写来着,对对对,落地就把 Map 换成外部存储的“按 userId 分桶”,或者用流处理(Flink/Kafka Streams)做window+maxBy,同理。好了不说了,关东煮糊了我先去换一份…
-END-
我为大家打造了一份RPA教程,完全免费:songshuhezi.com/rpa.html