我有以下信息列表
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
}
该列表需要以
的方式处理fieldOne
和fieldTwo
的值相等时,条目被视为重复。fieldThree
和fieldFour
的连接值。我想处理这个列表Java8 Streams。目前,我不知道如何根据自定义字段删除重复项。我想我不能使用distinct()
,因为我不能改变equals / hashcode方法,因为逻辑只适用于这种特定情况。
我怎样才能做到这一点?
答案 0 :(得分:3)
假设你有
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());
收集器更易于使用的情况之一,尤其是因为组合元素的合并不会从可变缩减中受益:
import dask
dask.set_options(get=dask.threaded.get)
这使用相同的函数来确定键,另一个函数确定单个值,如果一个组有多个元素,它只是一个身份函数和一个将被调用的简化函数。这将再次成为根据ID比较器返回最小值的函数。
答案 1 :(得分:2)
使用流,如果为其提供适当的分类器,则只需使用收集器即可处理它:
private static <T> T min(T first, T second, Comparator<? super T> cmp) {
return cmp.compare(first, second) <= 0 ? first : second;
}
private static void process(Collection<TheInfo> data) {
Comparator<TheInfo> cmp = Comparator.comparing(info -> info.id);
data.stream()
.collect(Collectors.toMap(
info -> Arrays.asList(info.fieldOne, info.fieldTwo), // Your classifier uses a tuple. Closest thing in JDK currently would be a list or some custom class. I chose List for brevity.
info -> info, // or Function.identity()
(a, b) -> min(a, b, cmp) // what do we do with duplicates. Currently we take min according to Comparator.
));
}
上面的流将被收集到Map<List<String>, TheInfo>
中,其中包含最小元素,其中包含两个字符串列表作为键。您可以提取map.values()
然后返回新的集合或者您需要的任何内容。