我们组年终奖总共23.8w,小组就我和组长两个人,我只拿到 1.67w,组长却拿了22.13w
刚看到个贴子,说一网友吐槽:他们组年终奖一共 23.8 万,就俩人,他拿 1.67 万,组长拿了 22.13 万,直接气炸。
我觉得这事吧,先承认一句:情绪正常,谁看了不难受。网友们的回复我看了看,有的说“领导背锅多拿点很正常”,也有人骂公司黑心。怎么说呢,这两种说法都对一半。
我的看法是:先冷静,把“委屈”换成三个动作——问清规则、评估成长、决定去留。规则说不明白、成长看不到,那就别死磕,把时间留给更值得的地方。
面试题:按月统计订单数与顾客数
昨天晚上快下班的时候,产品突然甩过来一句:”按月看看订单量和顾客数呗,就很普通的统计。“听起来很普通对吧,但真落到代码上,很多同学要么写一坨 SQL,要么在 Java 里绕晕自己。
我就按这个小需求,顺手整理成一篇小记,顺带把思路和代码都讲清楚,用的就是 Java。
题目叫“按月统计订单数与顾客数”,翻译一下就是:
有一堆订单记录,每条起码有:订单 id、顾客 id、下单时间
需要按“年-月”这一维度,把数据分组
对每个月算两个东西:
这个月一共多少笔订单 这个月一共有多少不同的顾客(去重后的顾客数)
重点是“顾客数”要去重,同一个人下 10 单,也只算 1 个顾客。
数据可能来自数据库,也可能是接口查出来已经在内存里的 List,这里就按“内存 List 运算”来写,思路更通用。
想象一下你桌上放着一摞纸,每张纸是一笔订单,你要做一个“按月汇总”的小本子:
从头到尾把订单一张张翻过去
对于每一张:
看它的下单时间,拿到对应的“年-月”,比如 2024-01 在小本子里找到这一个月那一页,没有就新建一页 这一页上的“订单数 +1” 把这个订单的顾客 id 记进一个“顾客集合”里(Set,天然去重)
等所有订单扫完,再把每一页上的“顾客集合.size()”取出来,就是这个月的顾客数
所以伪代码结构就是:
用一个 Map<YearMonth, Stats>做中间结果Stats里有:orderCount和Set<Long> customerIds单次扫描订单列表,时间复杂度 O(n)
这个思路基本可以直接翻译成 Java 代码。
先随便定义一个订单类,真实项目里你按自己实体来就行:
import java.time.LocalDateTime;
publicclassOrder{
private Long orderId;
private Long customerId;
private LocalDateTime createTime;
publicOrder(Long orderId, Long customerId, LocalDateTime createTime){
this.orderId = orderId;
this.customerId = customerId;
this.createTime = createTime;
}
public Long getOrderId(){
return orderId;
}
public Long getCustomerId(){
return customerId;
}
public LocalDateTime getCreateTime(){
return createTime;
}
}
中间统计用一个小类装一下:
import java.util.HashSet;
import java.util.Set;
classMonthlyStatsTmp{
int orderCount;
Set<Long> customerIds = new HashSet<>();
}
对外返回的结构可以稍微体面一点,比如带上年月、订单数、顾客数:
import java.time.YearMonth;
publicclassMonthlyStats{
private YearMonth month;
privateint orderCount;
privateint customerCount;
publicMonthlyStats(YearMonth month, int orderCount, int customerCount){
this.month = month;
this.orderCount = orderCount;
this.customerCount = customerCount;
}
public YearMonth getMonth(){
return month;
}
publicintgetOrderCount(){
return orderCount;
}
publicintgetCustomerCount(){
return customerCount;
}
@Override
public String toString(){
return month + " => orders=" + orderCount + ", customers=" + customerCount;
}
}
核心方法来了,一眼能看懂那种:
import java.time.YearMonth;
import java.util.*;
publicclassOrderStatsService{
/**
* 按“年-月”统计订单数和去重顾客数
*/
publicstatic List<MonthlyStats> statByMonth(List<Order> orders){
// 先用 HashMap 做中间累加
Map<YearMonth, MonthlyStatsTmp> tmpMap = new HashMap<>();
for (Order order : orders) {
if (order == null || order.getCreateTime() == null) {
continue; // 简单防御
}
YearMonth ym = YearMonth.from(order.getCreateTime());
MonthlyStatsTmp stats = tmpMap.computeIfAbsent(ym, k -> new MonthlyStatsTmp());
stats.orderCount++;
stats.customerIds.add(order.getCustomerId());
}
// 转成对外的结果,并按时间排序一下
List<MonthlyStats> result = new ArrayList<>();
tmpMap.entrySet().stream()
.sorted(Map.Entry.comparingByKey()) // YearMonth 自带排序
.forEach(e -> {
YearMonth ym = e.getKey();
MonthlyStatsTmp s = e.getValue();
result.add(new MonthlyStats(ym, s.orderCount, s.customerIds.size()));
});
return result;
}
}
如果你是用老的 java.util.Date 或 Timestamp 存时间,也没关系,转换成 LocalDateTime 或直接手工取 year 和 month 都行,比如:
// 假设有一个 Date createTime
Calendar calendar = Calendar.getInstance();
calendar.setTime(createTime);
int year = calendar.get(Calendar.YEAR);
int month = calendar.get(Calendar.MONTH) + 1; // 注意从 0 开始
String ymKey = year + "-" + (month < 10 ? "0" + month : month);
// 然后 Map<String, MonthlyStatsTmp> 也一样能做
真实项目里,如果数据量特别大,或者本来就要查数据库,其实可以让数据库先按月 group by 一层,再在 Java 里做一点补充处理;上面这种写法更适合“订单已经在内存里了,我顺手统计一下”的那种场景。
-END-
我为大家打造了一份RPA教程,完全免费:songshuhezi.com/rpa.html