2014-09-25 47 views
4

我想將對象列表轉換爲映射,其中映射的鍵和值位於列表中的對象的列表中。Java 8列表<T>納入映射<K, V>

這裏Java 7的片斷這樣convertation的:

private Map<String, Child> getChildren(List<Family> families ) { 
     Map<String, Child> convertedMap = new HashMap<String, Child>(); 

     for (Family family : families) { 
      convertedMap.put(family.getId(), family.getParent().getChild()); 
     } 
     return convertedMap; 
    } 

回答

10

應該是類似的東西...

Map<String, Child> m = families.stream() 
    .collect(Collectors.toMap(Family::getId, f -> f.getParent().getChild())); 
8

傑森給了decent answer(+1),但我要指出,這與OP的Java 7代碼有不同的語義。該問題涉及如果輸入列表中的兩個家庭實例具有重複ID的行爲。也許他們保證獨一無二,在這種情況下沒有任何區別。但是,如果有重複,則使用OP的原始代碼,則稍後列表中的Family將覆蓋列表中具有相同ID的較早的Family的映射條目。

與Jason的代碼(如下圖所示,略有修改):

Map<String, Child> getChildren(List<Family> families) { 
    return families.stream() 
     .collect(Collectors.toMap(Family::getId, f -> f.getParent().getChild())); 
} 

Collectors.toMap操作將拋出IllegalStateException如果有任何重複鍵。這有點令人不快,但至少它會通知您存在重複,而不是可能會丟失數據。 Collectors.toMap(keyMapper, valueMapper)的規則是您需要確保鍵映射函數爲流的每個元素返回一個唯一鍵。

你需要做些什麼 - 如果有的話 - 取決於問題領域。一種可能性是使用三個參數版本:Collectors.toMap(keyMapper, valueMapper, mergeFunction)。這指定了一個額外的函數,在重複的情況下被調用。如果你想擁有較後的條目覆蓋前面的(匹配原始的Java代碼7),你可以這樣做:

Map<String, Child> getChildren(List<Family> families) { 
    return families.stream() 
     .collect(Collectors.toMap(Family::getId, f -> f.getParent().getChild(), 
               (child1, child2) -> child2)); 
} 

另一種方法是培養孩子對每個家庭的名單而不是僅僅的一個小孩。你可以編寫一個更復雜的合併函數,爲第一個孩子創建一個列表,並將其添加到第二個和後續孩子的列表中。這是很常見的,有一個特殊的groupingBy收集器可以自動執行此操作。本身就會產生一個按ID分組的家庭列表。我們不需要一個家庭列表,而是我們想要一個兒童列表,所以我們添加一個下游映射操作來映射從一個家庭到另一個孩子,然後將這些孩子收集到一個列表中。該代碼是這樣的:

Map<String, List<Child>> getChildren(List<Family> families) { 
    return families.stream() 
     .collect(Collectors.groupingBy(Family::getId, 
        Collectors.mapping(f -> f.getParent().getChild(), 
         Collectors.toList()))); 
} 

注意,返回類型已經從Map<String, Child>變更爲Map<String, List<Child>>

相關問題