2014-04-03 21 views
16

給定一個Map<String, Person>其中Person有一個String getName()(etc)方法,如何將Map<String, Person>變成Map<String, String>StringPerson::getName()獲得?使用流,我如何映射HashMap中的值?

前的Java 8我會使用

Map<String, String> byNameMap = new HashMap<>(); 

for (Map.Entry<String, Person> person : people.entrySet()) { 
    byNameMap.put(person.getKey(), person.getValue().getName()); 
} 

,但我想用流和lambda表達式做的事。

我看不到如何在功能樣式中執行此操作:Map/HashMap不執行Stream

people.entrySet()返回我可以流過的Set<Entry<String, Person>>,但是如何將新的Entry<String, String>添加到目標地圖?

回答

18

與Java 8,你可以這樣做:

Map<String, String> byNameMap = new HashMap<>(); 
people.forEach((k, v) -> byNameMap.put(k, v.getName()); 

雖然你使用會更好番石榴的Maps.transformValues,它包裝原Map並在執行get時進行轉換,這意味着只有在實際使用該值時才支付轉換成本。

使用番石榴應該是這樣的:

Map<String, String> byNameMap = Maps.transformValues(people, Person::getName); 

編輯:

繼@ Eelco的評論(和完整性),轉換到地圖是更好下來Collectors.toMap這樣的:

Map<String, String> byNameMap = people.entrySet() 
    .stream() 
    .collect(Collectors.toMap(Map.Entry::getKey, (entry) -> entry.getValue().getName()); 
+0

我接受這個,因爲它非常整潔,並且沒有令人厭煩的循環。 Guave解決方案特別整潔。謝謝 –

+2

@DavidKerr請注意,第一個代碼是不可並行化的,因爲HashMap不是線程安全的,與使用收集器/ toMap來分割作業(如果該流並行)相反。番石榴的版本也是不可並行的。但如果這不是一個問題,那麼一定要用這個版本。 – assylias

+3

第一個解決方案不起作用。另一個使用toMap收集器的答案是。 – Eelco

18

一種方法是使用toMap收藏家:

import static java.util.stream.Collectors.toMap; 

Map<String, String> byNameMap = people.entrySet().stream() 
            .collect(toMap(Entry::getKey, 
                e -> e.getValue().getName())); 
+4

這是有點整齊,但這樣的問題有什麼好處嗎?我會說最初的例子是更清晰/更易讀的代碼。 –

+3

@CharlesGoodwin可讀性一旦習慣了,雖然for循環可能更易於閱讀。 lambda的一個好處是,對於大型地圖,您可以使用'entrySet()。parallelStream()'並且輕鬆地平行處理作業。對於簡單的用例來說,循環很好。 – assylias

+1

@CharlesGoodwin我猜這隻會在你將更多的過濾和轉換放入地圖之前增加可讀性。 –