Java 8 Streams 减少删除重复项,保留最新条目
Posted
技术标签:
【中文标题】Java 8 Streams 减少删除重复项,保留最新条目【英文标题】:Java 8 Streams reduce remove duplicates keeping the most recent entry 【发布时间】: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()))
中。
【参考方案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
第一步:确保将date
s 与映射到id
的最近一个进行比较。
第二步:将这些键、值对映射到最终的List<EmployeeContract>
。
【讨论】:
为什么(a, b) -> 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() 等方法以上是关于Java 8 Streams 减少删除重复项,保留最新条目的主要内容,如果未能解决你的问题,请参考以下文章