用函数式响应式方法合并两个列表

3
让我们想象一下以下的对象:

假设有一个对象:

class People {
  public int id;
  public String name;
  public Date dateOfDeath;
}

我有两个人员列表。

在第一个列表中,People对象的ID和NAME已正确设置。在第二个列表中,People对象的ID和DATEOFDEATH已正确设置。

我需要将这两个列表合并,以获得一个完整的People对象列表(包括姓名和死亡日期)。

使用完全过程化的方法,可以像这样使用双重循环完成:

for (People fullPeople : firstList) {
  for (People peopleWithDateOfDeath : secondList) {
    if (peopleWithDateOfDeath.id == fullPeople.id) {
      fullPeople.dateOfDeath = peopleWithDateOfDeath.dateOfDeath;
      break;
    }
  }
}
secondList = null;
// first list is good :)

我该如何以函数式的方式实现这个功能?我正在使用Rx-Java,但使用Java 8的Streams也可以轻松转换。

2个回答

4

您可以通过构建 iddateOfDeath 的映射表来避免 O(n2) 复杂度:

Map<Integer, Date> deaths = secondList.stream()
    .collect(toMap(p -> p.id, p -> p.dateOfDeath));

fullPeople.stream()
    .filter(p -> deaths.containsKey(p.id))
    .forEach(p -> p.dateOfDeath = deaths.get(p.id));

或者,如果你想避免改变现有的对象:

List<People> mergedPeople = fullPeople.stream()
    .map(p -> deaths.containsKey(p.id) 
            ? new People(p.id, p.name, deaths.get(p.id))
            : p
    ).collect(toList());

2
您可以这样做:
List<People> persons = 
        names.stream()
             .map(p -> new People(p.id, p.name, dates.stream()
                                                     .filter(pd -> pd.id == p.id)
                                                     .map(pd -> pd.dateOfDeath)
                                                     .findFirst()
                                                     .orElse(null))
             )
             .collect(Collectors.toList());

其中names是拥有姓名的人员列表,dates是拥有死亡日期的人员列表。假设People类具有一个三个参数的构造函数,取id、姓名和死亡日期。

对于所有带有姓名的人员,使用filter在另一个列表中查找具有相同id的人员,并将结果映射到dateOfDeath。如果找到匹配项,则返回日期,否则调用orElse并返回null

请注意,这不会合并任何存在于dates列表中但不存在于names列表中的人员。

示例代码:

List<People> names = new ArrayList<>();
List<People> dates = new ArrayList<>();
names.add(new People(1, "Name 1", null));
names.add(new People(2, "Name 2", null));
dates.add(new People(1, null, new Date()));
dates.add(new People(3, null, new Date()));

List<People> peoples = codeFromAbove();
System.out.println(peoples);
// prints
// [[id=1, name=Name 1, date=Sun Oct 18 19:48:58 CEST 2015],
// [id=2, name=Name 2, date=null]]

使用:

class People {
    public int id;
    public String name;
    public Date dateOfDeath;
    public People(int id, String name, Date dateOfDeath) {
        this.id = id;
        this.name = name;
        this.dateOfDeath = dateOfDeath;
    }
    @Override
    public String toString() {
        return "[id="+id+", name="+name+", date="+dateOfDeath+"]";
    }
}

感谢您提供这个简洁的示例。在过程化方法中,可以通过删除已匹配的人员来优化内部循环,以避免不断深入搜索日期列表。在我的情况下,这可能会有很大帮助,因为我知道两个列表都将按ID排序。在函数式方法中是否有进行相同优化的任何方法? - pdegand59
1
@pdegand59 Misha的回答可以为您完成此操作:您可以调用deaths.remove(p.id)而不是deaths.get(p.id) - Tunaki

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接