Ich weiß, wie man ein einfaches Java List
von Y
-> "transformiert" Z
, dh:
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 auf String
-> beschränkt sein Integer
. Genau wie im List
obigen Beispiel möchte ich eine beliebige Methode (oder einen Konstruktor) aufrufen.
java
mapreduce
java-8
java-stream
collectors
Benjamin M.
quelle
quelle
e -> e.getKey()
mitMap.Entry::getKey
. Aber das ist eine Frage des Geschmacks / Programmierstils.Hier sind einige Variationen der Antwort von Sotirios Delimanolis , die anfangs ziemlich gut war (+1). Folgendes berücksichtigen:
Ein paar Punkte hier. Erstens ist die Verwendung von Platzhaltern in den Generika; Dies macht die Funktion etwas flexibler. Ein Platzhalter wäre erforderlich, wenn Sie beispielsweise möchten, dass die Ausgabekarte einen Schlüssel enthält, der eine Oberklasse des Schlüssels der Eingabekarte 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 Randfällen hilfreich ist.)
Ein zweiter Punkt ist, dass
entrySet
ich den Stream nicht über die Eingabe-Map laufen ließ , sondern über diekeySet
. Dies macht den Code meiner Meinung nach ein wenig sauberer, da Werte aus der Karte anstatt aus dem Karteneintrag abgerufen werden müssen. Übrigens hatte ich zunächstkey -> key
als erstes Argument dazutoMap()
und dies schlug aus irgendeinem Grund mit einem Typinferenzfehler fehl. Es(X key) -> key
funktionierte genauso wie es funktionierteFunction.identity()
.Eine weitere Variation ist wie folgt:
Dies verwendet
Map.forEach()
anstelle von Streams. Das ist noch einfacher, denke ich, weil es auf die Sammler verzichtet, deren Verwendung mit Karten etwas umständlich ist. Der Grund dafür ist, dassMap.forEach()
der Schlüssel und der Wert als separate Parameter angegeben werden, während der Stream nur einen Wert hat - und Sie müssen auswählen, ob Sie den Schlüssel oder den Karteneintrag als diesen Wert verwenden möchten. Auf der negativen Seite fehlt dies die reiche, strömende Güte der anderen Ansätze. :-)quelle
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.Eine generische Lösung wie diese
Beispiel
quelle
Guavas Funktion
Maps.transformValues
ist genau das, wonach Sie suchen, und sie funktioniert gut mit Lambda-Ausdrücken:quelle
java.util.Function
, haben Sie zwei Möglichkeiten. 1. Vermeiden Sie das Problem, indem Sie ein Lambda verwenden, damit Java-Typ-Inferenz es herausfinden kann. 2. Verwenden Sie eine Methodenreferenz wie javaFunction :: apply, um ein neues Lambda zu erstellen, das durch Typinferenz ermittelt werden kann.Muss es unbedingt 100% funktional und fließend sein? Wenn nicht, wie wäre es damit, das so kurz wie möglich ist:
( wenn Sie mit der Schande und Schuld leben können, Streams mit Nebenwirkungen zu kombinieren )
quelle
Meine StreamEx- Bibliothek, die die Standard-Stream-API erweitert, bietet eine
EntryStream
Klasse, die sich besser zum Transformieren von Karten eignet :quelle
Eine Alternative, die es zu Lernzwecken immer gibt, besteht darin, Ihren benutzerdefinierten Kollektor über Collector.of () zu erstellen, obwohl der JDK-Kollektor toMap () hier kurz und bündig ist (+1 hier ).
quelle
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
Werte beim Reduzieren verloren gehen.Wenn es Ihnen nichts ausmacht, Bibliotheken von Drittanbietern zu verwenden, enthält meine Cyclops-React- Bibliothek Erweiterungen für alle JDK-Sammlungstypen , einschließlich Map . Wir können die Karte einfach direkt mit dem Operator 'map' transformieren (standardmäßig wirkt sich die Map auf die Werte in der Map aus).
Mit bimap können die Schlüssel und Werte gleichzeitig transformiert werden
quelle
Die deklarative und einfachere Lösung wäre:
yourMutableMap.replaceAll ((Schlüssel, Wert) -> return_value_of_bi_your_function); Nb. Beachten Sie, dass Sie Ihren Kartenstatus ändern. Das ist vielleicht nicht das, was Sie wollen.
Prost an: http://www.deadcoderising.com/2017-02-14-java-8-declarative-ways-of-modifying-a-map-using-compute-merge-and-replace/
quelle