Wiem, jak „przekształcić” prostą Javę List
z Y
-> Z
, tj .:
List<String> x;
List<Integer> y = x.stream()
.map(s -> Integer.parseInt(s))
.collect(Collectors.toList());
Teraz chciałbym zrobić w zasadzie to samo z Mapą, tj .:
INPUT:
{
"key1" -> "41", // "41" and "42"
"key2" -> "42 // are Strings
}
OUTPUT:
{
"key1" -> 41, // 41 and 42
"key2" -> 42 // are Integers
}
Rozwiązanie nie powinno ograniczać się do String
-> Integer
. Tak jak w List
powyższym przykładzie, chciałbym wywołać dowolną metodę (lub konstruktor).
java
mapreduce
java-8
java-stream
collectors
Benjamin M.
źródło
źródło
e -> e.getKey()
zMap.Entry::getKey
. Ale to kwestia gustu / stylu programowania.Oto kilka wariantów odpowiedzi Sotirios Delimanolis , która na początek była całkiem dobra (+1). Rozważ następujące:
Kilka punktów tutaj. Pierwszym z nich jest użycie symboli wieloznacznych w nazwach ogólnych; dzięki temu funkcja jest nieco bardziej elastyczna. Symbol wieloznaczny byłby konieczny, jeśli na przykład chcesz, aby mapa wyjściowa miała klucz, który jest nadklasą klucza mapy wejściowej:
(Jest też przykład wartości mapy, ale jest ona naprawdę zmyślona i przyznaję, że ograniczenie znaku wieloznacznego dla Y pomaga tylko w skrajnych przypadkach.)
Drugim punktem jest to, że zamiast przepuszczać strumień nad mapą wejściową
entrySet
, uruchomiłem go nadkeySet
. To sprawia, że kod jest nieco bardziej przejrzysty, kosztem konieczności pobierania wartości z mapy zamiast z wpisu mapy. Nawiasem mówiąc, początkowo miałemkey -> key
jako pierwszy argument,toMap()
a to z jakiegoś powodu nie powiodło się z błędem wnioskowania typu. Zmieniłem to na(X key) -> key
działające, tak jak to zrobiłemFunction.identity()
.Jeszcze inna odmiana jest następująca:
To używa
Map.forEach()
zamiast strumieni. Sądzę, że jest to jeszcze prostsze, ponieważ rezygnuje z kolektorów, których używanie w mapach jest nieco niezdarne. Powodem jest to, żeMap.forEach()
podaje klucz i wartość jako osobne parametry, podczas gdy strumień ma tylko jedną wartość - i musisz wybrać, czy użyć klucza, czy wpisu mapy jako tej wartości. Z drugiej strony brakuje w tym bogatej, gwałtownej dobroci innych podejść. :-)źródło
Function.identity()
może wyglądać fajnie, ale ponieważ pierwsze rozwiązanie wymaga wyszukiwania mapy / skrótu dla każdego wpisu, podczas gdy wszystkie inne rozwiązania nie, nie polecam tego.Takie ogólne rozwiązanie
Przykład
źródło
Funkcja Guava
Maps.transformValues
jest tym, czego szukasz, i ładnie działa z wyrażeniami lambda:źródło
java.util.Function
, masz dwie opcje. 1. Unikaj tego problemu, używając lambda, aby umożliwić wnioskowanie typu Java. 2. Użyj odwołania do metody, takiego jak javaFunction :: Apply, aby utworzyć nową lambda, którą można wywnioskować na podstawie wnioskowania typu.Czy to absolutnie musi być w 100% funkcjonalne i płynne? Jeśli nie, co powiesz na to, co jest tak krótkie, jak tylko się da:
( jeśli potrafisz żyć ze wstydem i winą łączenia strumieni z efektami ubocznymi )
źródło
Moja biblioteka StreamEx, która ulepsza standardowy interfejs API strumienia, zapewnia
EntryStream
klasę, która lepiej pasuje do przekształcania map:źródło
Alternatywą, która zawsze istnieje w celu uczenia się, jest zbudowanie własnego kolektora za pomocą Collector.of (), chociaż tutaj toMap () kolektor JDK jest zwięzły (+1 tutaj ).
źródło
map2.entrySet().forEach(entry -> { if (map1.containsKey(entry.getKey())) { map1.get(entry.getKey()).merge(entry.getValue()); } else { map1.put(entry.getKey(),entry.getValue()); } }); return map1
przeciwnym razie wartości zostaną utracone podczas zmniejszania.Jeśli nie masz nic przeciwko korzystaniu z bibliotek stron trzecich, moja biblioteka Cyclops- React ma rozszerzenia dla wszystkich typów kolekcji JDK , w tym Map . Możemy po prostu przekształcić mapę bezpośrednio za pomocą operatora „map” (domyślnie mapa działa na wartości na mapie).
bimap może być używany do transformacji kluczy i wartości jednocześnie
źródło
Deklaratywnym i prostszym rozwiązaniem byłoby:
yourMutableMap.replaceAll ((key, val) -> return_value_of_bi_your_function); Uwaga pamiętaj, że modyfikujesz stan mapy. Więc to może nie być to, czego chcesz.
Pozdrawiam: http://www.deadcoderising.com/2017-02-14-java-8-declarative-ways-of-modifying-a-map-using-compute-merge-and-replace/
źródło