Java 组合两个列表以形成唯一列表(基于某些字段逻辑)

Java 组合两个列表以形成唯一列表(基于某些字段逻辑),java,list,lambda,java-8,Java,List,Lambda,Java 8,我有一个具体的问题要解决,需要一些帮助 我有一个包含三个字段的POJO: 身份证 日期 加权数 此POJO可以从DB或外部端点填充,并将返回所述POJO的列表。 现在我们得到两个信息相似的独立列表。这些列表将无序且大小不一(有时为空) 如何结合这两个列表,根据ID创建一个唯一的列表? 如果ID相同,请选择具有最新日期的ID 如果ID和日期相同,请选择加权数较高的ID和日期 如果所有的都是一样的,那也没关系——任选一个 我的解决方案非常麻烦,阅读起来也很难看——它包含两个嵌套循环和3对if

我有一个具体的问题要解决,需要一些帮助

我有一个包含三个字段的POJO:

  • 身份证
  • 日期
  • 加权数
此POJO可以从DB或外部端点填充,并将返回所述POJO的列表。 现在我们得到两个信息相似的独立列表。这些列表将无序且大小不一(有时为空)

如何结合这两个列表,根据ID创建一个唯一的列表?

  • 如果ID相同,请选择具有最新日期的ID
  • 如果ID和日期相同,请选择加权数较高的ID和日期
  • 如果所有的都是一样的,那也没关系——任选一个
我的解决方案非常麻烦,阅读起来也很难看——它包含两个嵌套循环和3对if-else语句。有没有更好、更有效的方法

以下是一些示例代码:

import java.util.Arrays;
import java.util.Calendar;
import java.util.Date;
import java.util.List;

public class Scrap2 {

    public static void main(String[] args) {

        MyEntity one = new MyEntity("A", new Date(2020 - 1900, Calendar.SEPTEMBER, 25), 10);
        MyEntity two = new MyEntity("B", new Date(2020 - 1900, Calendar.SEPTEMBER, 25), 10);
        MyEntity three = new MyEntity("C", new Date(2020 - 1900, Calendar.SEPTEMBER, 25), 10);
        MyEntity four = new MyEntity("D", new Date(2020 - 1900, Calendar.SEPTEMBER, 25), 10);

        List<MyEntity> listOne = Arrays.asList(one, two, three, four);

        MyEntity aaa = new MyEntity("A", new Date(2020 - 1900, Calendar.SEPTEMBER, 25), 10);
        MyEntity bbb = new MyEntity("B", new Date(2020 - 1900, Calendar.OCTOBER, 25), 20);
        MyEntity ccc = new MyEntity("D", new Date(2020 - 1900, Calendar.SEPTEMBER, 25), 20);
        MyEntity ddd = new MyEntity("E", new Date(2020 - 1900, Calendar.SEPTEMBER, 25), 20);

        List<MyEntity> listTwo = Arrays.asList(aaa, bbb, ccc, ddd);

        for (MyEntity listItem : combineTwoLists(listOne, listTwo)) {
            System.out.println(listItem);
        }

    }

    private static List<MyEntity> combineTwoLists(List<MyEntity> listOne, List<MyEntity> listTwo) {
       
        return Arrays.asList(listOne.get(0), listTwo.get(1), listOne.get(2), listTwo.get(2), listTwo.get(3));
    }
}


class MyEntity {

    private final String id;
    private final Date date;
   private final Integer weightedNumber;

    public MyEntity(String id, Date date, Integer weightedNumber) {
        this.id = id;
        this.date = date;
        this.weightedNumber = weightedNumber;
    }

    @Override
    public String toString() {
        return "MyEntity{" +
                "id='" + id + '\'' +
                ", date=" + date +
                ", weightedNumber=" + weightedNumber +
                '}';
    }

    public String getId() {
        return id;
    }

    public Date getDate() {
        return date;
    }

    public Integer getWeightedNumber() {
        return weightedNumber;
    }
}

您可以先加入两个列表,然后使用
Comparator
BinaryOperator.maxBy
获取同一id的max by date,然后使用
Collectors.toMap
创建映射。然后在新的ArrayList中获取映射的值

return new ArrayList<MyEntity>(
    Stream.concat(listOne.stream(), listTwo.stream())
          .collect(Collectors.toMap(MyEntity::getId, Function.identity(),
                   BinaryOperator.maxBy(Comparator.comparing(MyEntity::getDate)
                                          .thenComparing(MyEntity::getWeightedNumber))))
          .values());
返回新的ArrayList(
Stream.concat(listOne.Stream(),listwo.Stream())
.collect(Collectors.toMap(MyEntity::getId,Function.identity(),
maxBy(Comparator.comparing(MyEntity::getDate)
.Then比较(MyEntity::getWeightedNumber)))
.values());

以下使用
收集器的方法。groupingBy
收集器。maxBy
应该可以工作:

  • 连接两个列表的流
  • 使用
    groupingBy
  • 然后使用
    maxBy
    收集器按
    date
    weightedNumber
    进行分组
  • map.Entry
    的结果映射转换为
    List
  • 私有静态列表组合策略(列表一、列表二){
    返回Stream.concat(listOne.Stream(),listwo.Stream())
    .collect(收集器).groupingBy(MyEntity::getId、,
    收藏家.maxBy(
    Comparator.comparing(MyEntity::getDate)
    .Then比较(MyEntity::getWeightedNumber))
    )).entrySet()
    .stream()//流
    .map(map.Entry::getValue)//获取可选值
    .map(可选::get)//从可选
    .collect(Collectors.toList());
    }
    
    这里有另一种方法可以做到这一点,没有流,但功能强大:

    Map<String, MyEntity> map = new LinkedHashMap<>();
    Consumer<MyEntity> addAction = e -> map.merge(
            e.getId(), 
            e, 
            BinaryOperator.maxBy(Comparator.comparing(MyEntity::getDate)
                                           .thenComparing(MyEntity::getWeightedNumber)));
    listOne.forEach(addAction);
    listTwo.forEach(addAction);
    
    List<MyEntity> result = new ArrayList<>(map.values());
    
    Map Map=newlinkedhashmap();
    消费者添加操作=e->map.merge(
    e、 getId(),
    E
    maxBy(Comparator.comparing(MyEntity::getDate)
    。然后比较(MyEntity::getWeightedNumber));
    listOne.forEach(addAction);
    清单2.forEach(addAction);
    列表结果=新的ArrayList(map.values());
    

    这将迭代两个列表,并在每个元素上执行
    addAction
    使用者。
    addAction
    使用者通过应用该方法将实体合并到地图中。

    两个列表的顺序和/或大小是否始终相同?@jamiebiotti否,顺序和大小可能不同。甚至可能有一个列表为空的情况。如果结果不需要排序,并且可能是无序/未排序的。您的解决方案将如何更改?如果您不想在合并列表之前排序默认顺序是什么?没有默认顺序。这些列表要么来自DB,要么来自RESTAPI——它们的顺序可能会有很大的差异。有时甚至是空的。我的意思是你们想保持列表中的任何顺序吗?如果不是,为什么你不想排序?投票赞成使用
    BinaryOperator.maxBy
    这是一个鲜为人知的非常好的实用程序
    Map<String, MyEntity> map = new LinkedHashMap<>();
    Consumer<MyEntity> addAction = e -> map.merge(
            e.getId(), 
            e, 
            BinaryOperator.maxBy(Comparator.comparing(MyEntity::getDate)
                                           .thenComparing(MyEntity::getWeightedNumber)));
    listOne.forEach(addAction);
    listTwo.forEach(addAction);
    
    List<MyEntity> result = new ArrayList<>(map.values());