Java8: HashMap zu HashMap mit Stream / Map-Reduce / Collector
Lesezeit: 5 Minuten
Benjamin m
Ich weiß, wie man ein einfaches Java “transformiert”. List aus Y -> Zdh:
List<String> x;
List<Integer> y = x.stream()
.map(s -> Integer.parseInt(s))
.collect(Collectors.toList());
Jetzt möchte ich im Grunde dasselbe mit einer Karte machen, dh:
INPUT:
{
"key1" -> "41", // "41" and "42"
"key2" -> "42" // are Strings
}
OUTPUT:
{
"key1" -> 41, // 41 and 42
"key2" -> 42 // are Integers
}
Die Lösung sollte nicht darauf beschränkt sein String -> Integer. Genau wie im List Beispiel oben möchte ich eine beliebige Methode (oder einen Konstruktor) aufrufen.
John Kugelmann
Map<String, String> x;
Map<String, Integer> y =
x.entrySet().stream()
.collect(Collectors.toMap(
e -> e.getKey(),
e -> Integer.parseInt(e.getValue())
));
Es ist nicht ganz so schön wie der Listencode. Neu konstruieren kann man nicht Map.Entrys in a map() Anruf, damit die Arbeit in die gemischt wird collect() Anruf.
Sie können ersetzen e -> e.getKey() mit Map.Entry::getKey. Aber das ist Geschmackssache/Programmierstil.
– Holger
18. September 2014 um 9:49 Uhr
Eigentlich ist es eine Frage der Leistung, Sie schlagen vor, dem Lambda-Stil etwas überlegen zu sein.
– Jon Burgin
20. April 2017 um 20:35 Uhr
Stuart Mark
Hier sind einige Variationen der Antwort von Sotirios Delimanolis, die anfangs ziemlich gut war (+1). Folgendes berücksichtigen:
Ein paar Punkte hier. Das erste ist die Verwendung von Platzhaltern in den Generika; dadurch wird die Funktion etwas flexibler. Ein Platzhalter wäre beispielsweise erforderlich, wenn Sie möchten, dass die Ausgabezuordnung einen Schlüssel hat, der eine Oberklasse des Schlüssels der Eingabezuordnung ist:
(Es gibt auch ein Beispiel für die Werte der Karte, aber es ist wirklich erfunden, und ich gebe zu, dass der begrenzte Platzhalter für Y nur in Grenzfällen hilft.)
Ein zweiter Punkt ist, dass der Stream nicht über die Eingabekarte läuft entrySetlief ich es über die keySet. Das macht den Code ein wenig sauberer, denke ich, auf Kosten der Notwendigkeit, Werte aus der Map statt aus dem Map-Eintrag zu holen. Übrigens hatte ich anfangs key -> key als erstes Argument zu toMap() und dies schlug aus irgendeinem Grund mit einem Typrückschlussfehler fehl. Ändern Sie es zu (X key) -> key funktionierte, wie auch Function.identity().
Dies nutzt Map.forEach() statt Strömen. Das ist noch einfacher, finde ich, weil es auf die etwas umständlich zu handhabenden Kollektoren mit Karten verzichtet. Der Grund ist, dass Map.forEach() gibt den Schlüssel und den Wert als separate Parameter an, während der Stream nur einen Wert hat – und Sie müssen wählen, ob Sie den Schlüssel oder den Karteneintrag als diesen Wert verwenden möchten. Auf der Minusseite fehlt die reichhaltige, fließende Güte der anderen Ansätze. 🙂
Function.identity() mag cool aussehen, aber da die erste Lösung eine Map/Hash-Suche für jeden Eintrag erfordert, während alle anderen Lösungen dies nicht tun, würde ich es nicht empfehlen.
Mein StreamEx Bibliothek, die die Standard-Stream-API verbessert, bietet eine EntryStream Klasse, die besser zum Transformieren von Karten geeignet ist:
Eine Alternative, die immer zu Lernzwecken vorhanden ist, besteht darin, Ihren benutzerdefinierten Kollektor über Collector.of() zu erstellen, obwohl toMap() JDK-Kollektor hier prägnant ist (+1 hier) .
Ich habe mit diesem benutzerdefinierten Kollektor als Basis begonnen und wollte hinzufügen, dass der binaryOperator zumindest bei Verwendung von parallelStream() anstelle von stream() in etwas Ähnliches umgeschrieben werden sollte 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 oder Werte gehen beim Reduzieren verloren.
– Benutzer691154
14. Dezember 2016 um 9:41 Uhr
13575400cookie-checkJava8: HashMap zu HashMap mit Stream / Map-Reduce / Collectoryes