2014-04-03 12 views
16

Biorąc pod uwagę Map<String, Person> gdzie człowiek ma metodę String getName() (ETC) na to, w jaki sposób można włączyć Map<String, Person> do Map<String, String> gdzie String uzyskuje się z wywołaniem Person::getName()?Używając strumieni, jak mogę odwzorować wartości w HashMap?

Pre-Java 8 użyję

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

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

ale chciałbym to zrobić za pomocą strumieni i lambdy.

Nie widzę, jak to zrobić w stylu funkcjonalnym: Mapa/HashMap nie implementuje Stream.

people.entrySet() zwraca wartość Set<Entry<String, Person>>, którą mogę przesłać strumieniowo, ale jak dodać nową Entry<String, String> do mapy docelowej?

Odpowiedz

18

Z Java 8 można zrobić:

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

Mimo to lepiej wyłączyć za pomocą Guava na Maps.transformValues, który owija oryginalny Map i wykonuje konwersję, gdy robisz get, co oznacza, że ​​płacisz tylko koszt konwersji, gdy faktycznie zużywasz wartość.

Korzystanie Guava będzie wyglądać następująco:

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

EDIT:

Po @ komentarzu Eelco (oraz dla kompletności), konwersja na mapie jest lepszy dół Collectors.toMap jak ten:

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

Akceptuję to, ponieważ jest tak schludny i nie jest męczący dla pętli. Rozwiązanie Guave jest szczególnie zadbane. Dzięki –

+2

@DavidKerr Zauważ, że pierwszy kod nie może być ustawiony równolegle, ponieważ HashMap nie jest bezpieczny dla wątków, w przeciwieństwie do użycia kolektora/toMap, który podzieli zadanie, jeśli strumień jest równoległy. Wersja Guava nie może być również równoległa. Ale jeśli to nie jest problem, to z całą pewnością korzystaj z tej wersji. – assylias

+3

Pierwsze rozwiązanie nie działa. Inna odpowiedź, za pomocą kolektora toMap, jest. – Eelco

18

Jednym ze sposobów jest użycie toMap br

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

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

To jest miłe, ale czy jest jakaś przewaga w stosunku do przykładu cytowanego w pytaniu? Powiedziałbym, że oryginalny przykład był wyraźniejszy/bardziej czytelny kod. –

+3

@CharlesGoodwin czytelność jest ok, gdy się do tego przyzwyczaisz, chociaż pętla for jest prawdopodobnie łatwiejsza do odczytania. Jedną z premii lambda jest to, że dla dużej mapy można 'entrySet(). ParallelStream()' i łatwo powiązać zadanie. W przypadku prostego użycia pętla jest w porządku. – assylias

+1

@CharlesGoodwin Przypuszczam, że opłaca się tylko pod względem czytelności, gdy dodasz więcej filtrowania i transformacji przed umieszczeniem go na mapie. –

Powiązane problemy