2015-10-18 37 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());