我有以下信息的列表
public class TheInfo {
private int id;
private String fieldOne;
private String fieldTwo;
private String fieldThree;
private String fieldFour;
//Standard Getters, Setters, Equals, Hashcode, ToString methods
}
该列表需要以以下方式处理:
在重复项中,选择具有最小ID的重复项,然后删除其他重复项。在这种特定情况下,当条目的
fieldOne
和fieldTwo
值相等时,它们被视为重复项。获取
fieldThree
和fieldFour
的串联值。我要处理此列表Java8流。目前,我不知道如何根据自定义字段删除重复项。我认为我不能使用
distinct()
,因为我不能更改equals / hashcode方法,因为逻辑仅用于此特定情况。我该如何实现?
最佳答案
假设你有
List<TheInfo> list;
您可以使用
List<TheInfo> result = new ArrayList<>(list.stream().collect(
Collectors.groupingBy(info -> Arrays.asList(info.getFieldOne(), info.getFieldOne()),
Collectors.collectingAndThen(
Collectors.minBy(Comparator.comparingInt(TheInfo::getId)),
Optional::get))).values());
groupingBy
收集器根据功能确定结果的相等性来生成组。列表已经为一系列值实现了此功能,因此Arrays.asList(info.getFieldOne(), info.getFieldOne())
会生成合适的键。在Java 9中,您很可能会使用List.of(info.getFieldOne(), info.getFieldOne())
。groupingBy
的第二个参数是另一个确定如何处理组的收集器,Collectors.minBy(…)
将根据比较器将它们折叠到最小元素,而Comparator.comparingInt(TheInfo::getId)
是用于获取具有最小id的元素的正确比较器。不幸的是,
minBy
收集器生成的Optional
在没有元素的情况下将为空,但是由于我们知道组不能为空(首先不会创建没有元素的组),因此我们可以无条件地调用可选的get
来检索实际值。这就是将收集器包装在Collectors.collectingAndThen(…, Optional::get)
中的作用。现在,分组的结果是从该函数创建的键到具有最小ID的
Map
实例的TheInfo
映射。在values()
上调用Map
给出为Collection<TheInfo>
,并且由于您想要List
,最终的new ArrayList<>(collection)
会生成它。考虑一下,这可能是
toMap
收集器更易于使用的一种情况,尤其是在合并组元素无法从可变归约中受益的情况下:List<TheInfo> result = new ArrayList<>(list.stream().collect(
Collectors.toMap(
info -> Arrays.asList(info.getFieldOne(), info.getFieldOne()),
Function.identity(),
BinaryOperator.minBy(Comparator.comparingInt(TheInfo::getId)))).values());
它使用相同的函数来确定键,并且使用另一个函数来确定单个值,如果组中有多个元素,则该值仅是标识函数和归约函数。这将再次是根据ID比较器返回最小值的函数。