2015-10-18 36 views
3

让我们想象一下以下对象:合并2只列出了一个功能性反应方式

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

你可以做这样的:

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,名字和死亡日期。

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

感谢这个干净的例子。按照程序的方式,可以通过删除任何已经匹配的人来优化内部循环,以避免必须更深入地搜索日期列表。在我的情况下,这可以帮助很多,因为我知道这两个列表都将在ID上排序。任何方式在功能上做同样的优化? – pdegand59

+1

@ pdegand59 Misha的答案可以为你做到这一点:你可以调用'deaths.remove(p.id)'而不是'deaths.get(p.id)'。 – Tunaki

4

可以避免为O(n )通过构建地图的iddateOfDeath一个复杂:

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

或者,如果你想避免改变现有的PE ople:

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