Java 8 条流减少删除重复项,保留最新条目

Java 8 Streams reduce remove duplicates keeping the most recent entry

我有一个 Java bean,比如

class EmployeeContract {
    Long id;
    Date date;
    getter/setter
}

如果有一长串这些,其中我们有重复的 ID 但日期不同,例如:

1, 2015/07/07
1, 2018/07/08
2, 2015/07/08
2, 2018/07/09

如何减少这样的列表,只保留最近日期的条目,例如:

1, 2018/07/08
2, 2018/07/09

? 最好使用 Java 8...

我从类似的东西开始:

contract.stream()
         .collect(Collectors.groupingBy(EmployeeContract::getId, Collectors.mapping(EmployeeContract::getId, Collectors.toList())))
                    .entrySet().stream().findFirst();

这为我提供了各个组内的映射,但我不知道如何将其收集到结果列表中 - 恐怕我的流不是太强...

您可以分两步完成:

List<EmployeeContract> finalContract = contract.stream() // Stream<EmployeeContract>
        .collect(Collectors.toMap(EmployeeContract::getId, 
                EmployeeContract::getDate, (a, b) -> a.after(b) ? a : b)) // Map<Long, Date> (Step 1)
        .entrySet().stream() // Stream<Entry<Long, Date>>
        .map(a -> new EmployeeContract(a.getKey(), a.getValue())) // Stream<EmployeeContract>
        .collect(Collectors.toList()); // Step 2

第一步:确保将 date 与最近映射到 id 的比较。

第二步:将这些键、值对映射到最终的List<EmployeeContract>结果。

好吧,我只是将我的评论以答案的形式放在这里:

 yourList.stream()
         .collect(Collectors.toMap(
                  EmployeeContract::getId,
                  Function.identity(),
                  BinaryOperator.maxBy(Comparator.comparing(EmployeeContract::getDate)))
            )
         .values();

这会给你 Collection 而不是 List,如果你真的关心这个的话。

使用 vavr.io 你可以这样做:

var finalContract = Stream.ofAll(contract) //create io.vavr.collection.Stream
            .groupBy(EmployeeContract::getId)
            .map(tuple -> tuple._2.maxBy(EmployeeContract::getDate))
            .collect(Collectors.toList()); //result is list from java.util package

只是为了补充现有答案,如您所问:

how to collect that into a result list

这里有一些选项:

  • values() 换成 ArrayList:

    List<EmployeeContract> list1 = 
        new ArrayList<>(list.stream()            
                            .collect(toMap(EmployeeContract::getId,                                                                          
                                           identity(),
                                           maxBy(comparing(EmployeeContract::getDate))))
                            .values());
    
  • toMap 收集器包装成 collectingAndThen:

    List<EmployeeContract> list2 = 
        list.stream()
            .collect(collectingAndThen(toMap(EmployeeContract::getId,
                                             identity(),
                                             maxBy(comparing(EmployeeContract::getDate))),
                     c -> new ArrayList<>(c.values())));
    
  • 使用另一个流将 values 收集到新的 List

    List<EmployeeContract> list3 = 
        list.stream()
            .collect(toMap(EmployeeContract::getId,
                           identity(),
                           maxBy(comparing(EmployeeContract::getDate))))
            .values()
            .stream()
            .collect(toList());