傑森給了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>>
。