我有以下信息的列表

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的重复项,然后删除其他重复项。在这种特定情况下,当条目的fieldOnefieldTwo值相等时,它们被视为重复项。
获取fieldThreefieldFour的串联值。


我要处理此列表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比较器返回最小值的函数。

07-24 09:49
查看更多