以功能反应方式合并2个列表

时间:2015-10-18 17:25:51

标签: java java-8 java-stream reactive-programming rx-java

让我们想象下面的对象:

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

我有2个人名单。

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

我需要合并2个列表,以便拥有一个包含完整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 个答案:

答案 0 :(得分:4)

您可以通过构建iddateOfDeath的地图来避免O(n 2 )的复杂性:

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());

答案 1 :(得分: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类有一个3参数构造函数来获取id,name和死亡日期。

对于拥有姓名的所有人,使用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+"]";
    }
}