【问题标题】:Java 8 Streams reduce remove duplicates keeping the most recent entryJava 8 Streams 减少删除重复项,保留最新条目
【发布时间】:2019-04-29 11:56:15
【问题描述】:

我有一个 Java bean,比如

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

如果 a 有一个很长的列表,其中我们有重复的 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();

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

【问题讨论】:

  • 我想发布一个答案,但是这个答案关闭得太快了...yourList.stream() .collect(Collectors.toMap( EmployeeContract::getId, Function.identity(), BinaryOperator.maxBy(Comparator.comparing(EmployeeContract::getDate).reversed())) ) .values();
  • @Eugene 代替BinaryOperator.maxBy( … .reversed()),您可以使用BinaryOperator.minBy(…)。虽然在这种情况下,看起来 OP 想要maxBy,没有.reversed()
  • @Holger 并鉴于 this(values()) 将返回 Collection<EmployeeContract> 而不是 List<EmployeeContract>,有没有一种简洁的方法来解决这个问题?
  • 鉴于有一个有效的讨论并且这是一个善意的问题,也许值得放弃这个问题?
  • @nullpointer 如果它确实需要是 List,您可以 a) 将整个表达式包装在 new ArrayList<>( … ) 中或 b) 将收集器包装在 Collectors.collectingAndThen( …, m -> new ArrayList<>(m.values())) 中。

标签: java collections java-8 java-stream reduction


【解决方案1】:

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

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

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

【讨论】:

    【解决方案2】:

    您可以分两步完成:

    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
    

    第一步:确保将dates 与映射到id 的最近一个进行比较。

    第二步:将这些键、值对映射到最终的List&lt;EmployeeContract&gt;

    【讨论】:

    • 为什么(a, b) -&gt; a.after(b) ? a : b) 已经实现Date Comparable
    • 没有具体原因@Eugene,我只是在查看Date API 进行比较,发现使用after 在可读性方面要好一些。
    • 很好的答案,并附有分步说明,非常感谢!
    【解决方案3】:

    只是为了补充现有的答案,正如你所问的那样:

    如何将其收集到结果列表中

    这里有一些选项:

    • 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());
      

    【讨论】:

      【解决方案4】:

      使用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
      

      【讨论】:

      • 不确定与 java.util.stream 中的经典 Stream 的区别?
      • 在 io.vavr 集合中的对象是不可变的,它们有 map()、filter() 等方法
      猜你喜欢
      • 2021-11-16
      • 1970-01-01
      • 1970-01-01
      • 2020-07-05
      • 2023-03-21
      • 2012-09-30
      • 1970-01-01
      • 2014-04-29
      • 2020-07-08
      相关资源
      最近更新 更多