如何基于条件过滤列表,用Stream保留相同日期下value最大的对象?
实现方案
完全可以通过Java Streams实现该需求,更推荐使用Collectors.groupingBy分组收集的方式实现,逻辑清晰且线程安全,不需要额外自定义Predicate也可完成,具体实现如下:
前置假设
你的MyObject类已实现对应字段的getter方法:
class MyObject { private Date date; private int value; public Date getDate() { return date; } public int getValue() { return value; } }
方案1:分组收集法(推荐)
核心逻辑是先按日期对元素分组,每组只保留value最大的元素,最后提取分组结果转为列表:
import java.util.Comparator; import java.util.List; import java.util.stream.Collectors; // 你的原始列表 List<MyObject> originalList = ...; List<MyObject> filteredList = originalList.stream() .collect(Collectors.groupingBy( // 按日期分组 MyObject::getDate, // 每组取value最大的元素,解包Optional Collectors.collectingAndThen( Collectors.maxBy(Comparator.comparingInt(MyObject::getValue)), optional -> optional.orElseThrow(() -> new IllegalArgumentException("空分组异常")) ) )) // 提取所有分组的结果转成列表 .values() .stream() // JDK16以下版本替换为.collect(Collectors.toList()) .toList();
方案2:结合状态Predicate实现
如果需要和Predicate配合使用,可以自定义带状态的Predicate记录已经出现过的日期对应的最大value,但是注意该方式不支持并行流,存在线程安全风险:
import java.util.HashMap; import java.util.Map; import java.util.function.Predicate; Predicate<MyObject> maxValueFilter = new Predicate<>() { // 存储日期对应的已记录最大value private final Map<Date, Integer> dateMaxMap = new HashMap<>(); @Override public boolean test(MyObject obj) { // 当前日期未记录,或当前对象value大于已记录最大值时更新缓存并返回true if (!dateMaxMap.containsKey(obj.getDate()) || obj.getValue() > dateMaxMap.get(obj.getDate())) { dateMaxMap.put(obj.getDate(), obj.getValue()); return true; } return false; } }; List<MyObject> filteredList = originalList.stream() .filter(maxValueFilter) // 二次去重,移除同一个日期下先出现的小value元素 .collect(Collectors.groupingBy( MyObject::getDate, Collectors.collectingAndThen( Collectors.maxBy(Comparator.comparingInt(MyObject::getValue)), optional -> optional.orElseThrow() ) )) .values() .stream() .toList();
注意事项
- 作为分组key的
Date类需要正确重写equals()和hashCode()方法,JDK自带的java.util.Date已经满足要求,若为自定义日期类需额外实现。 - 涉及多线程并行流场景时,仅推荐使用方案1,状态Predicate在并行场景下会出现数据错乱。
内容的提问来源于stack exchange,提问作者qwertyqwerty
相关产品推荐
相关产品推荐

