如何使用Stream API按地点分组并按日期(忽略时间)统计事件数
问题:使用Stream API按地点分组并统计不同日期数量
需求
将Event对象列表按location分组,同一地点下统计**不同日期(忽略时间部分)**的数量,最终得到Map<String, Integer>类型结果:key为location,value为该地点的不同日期数。
Event类代码
public class Event { String location; String date; String count; public String getCount() { return count; } public void setCount(String count) { this.count = count; } public String getLocation() { return location; } public String getDate() { return date; } public void setLocation(String location) { this.location = location; } public void setDate(String date) { this.date = date; } }
测试数据
注意:原测试代码存在复用同一Event对象的问题,会导致列表中所有元素属性一致,正确的测试代码应该为每个事件创建新对象:
List<Event> events = new ArrayList<>(); Event event1 = new Event(); event1.setLocation("98.55.62.162"); event1.setDate("09/17/2022 12:05:43 PM"); events.add(event1); Event event2 = new Event(); event2.setLocation("98.55.62.162"); event2.setDate("09/16/2022 12:05:45 PM"); events.add(event2); Event event3 = new Event(); event3.setLocation("98.55.62.162"); event3.setDate("09/16/2022 12:05:47 PM"); events.add(event3); Event event4 = new Event(); event4.setLocation("98.55.62.162"); event4.setDate("09/15/2022 12:05:49 PM"); events.add(event4); Event event5 = new Event(); event5.setLocation("98.55.62.163"); event5.setDate("09/17/2022 12:05:31 PM"); events.add(event5); Event event6 = new Event(); event6.setLocation("98.55.62.163"); event6.setDate("09/16/2022 12:05:22 PM"); events.add(event6); Event event7 = new Event(); event7.setLocation("98.55.62.163"); event7.setDate("09/16/2022 12:05:11 PM"); events.add(event7);
尝试过的代码
Map<String, List<Event>> eventMap = events.stream().collect(Collectors.groupingBy(s -> s.getLocation()));
该代码仅实现了按地点分组,未统计不同日期的数量。
期望输出
{"98.55.62.162":3 , "98.55.62.163":2}
解决方案
1. 使用Stream API实现
核心思路:按地点分组后,提取每个事件的日期部分(忽略时间),用Set去重后统计集合大小。
首先定义一个健壮的日期提取方法(避免字符串截取的兼容性问题):
private static String extractDatePart(String dateStr) { // 匹配测试数据的日期格式:MM/dd/yyyy hh:mm:ss a DateTimeFormatter formatter = DateTimeFormatter.ofPattern("MM/dd/yyyy hh:mm:ss a"); LocalDateTime dateTime = LocalDateTime.parse(dateStr, formatter); // 返回仅包含日期部分的字符串 return dateTime.toLocalDate().toString(); }
然后通过Stream API完成分组和统计:
Map<String, Integer> locationDateCount = events.stream() .collect(Collectors.groupingBy( Event::getLocation, // 按location分组 Collectors.collectingAndThen( Collectors.mapping( event -> extractDatePart(event.getDate()), // 提取日期部分 Collectors.toSet() // 存入Set自动去重 ), Set::size // 统计去重后的日期数量 ) ));
2. 使用循环实现
如果偏好传统循环方式,也可以通过Map+Set的组合实现:
// 先收集每个地点对应的所有唯一日期 Map<String, Set<String>> locationDateSet = new HashMap<>(); for (Event event : events) { String location = event.getLocation(); String datePart = extractDatePart(event.getDate()); // 若地点不存在则创建新的Set,否则直接添加日期 locationDateSet.computeIfAbsent(location, k -> new HashSet<>()).add(datePart); } // 转换为最终的统计结果 Map<String, Integer> locationDateCount = new HashMap<>(); for (Map.Entry<String, Set<String>> entry : locationDateSet.entrySet()) { locationDateCount.put(entry.getKey(), entry.getValue().size()); }
内容的提问来源于stack exchange,提问作者Jordan
相关产品推荐
相关产品推荐

