[Java 8+] Karten zusammenführen

Dinge die zu tun sind

Ziehen Sie in Betracht, mehrere Karten zu einer Karte zusammenzuführen. Schließlich wird es auf die folgenden Dienstprogrammmethoden verallgemeinert.

// Map<K, V>[]Karte<K, V>Vereinen
public static <K, V> Map<K, V> merge(BiFunction<? super V, ? super V, ? extends V> mergeFunction, Map<K, V>... maps)

// Map<K, V>[]Karte<K, R>Vereinen
public static <K, V, R> Map<K, R> merge(Collector<V, ?, R> mergeCollector, Map<K, V>... maps)

// Map<K, List<V>>[]Karte<K, List<V>>Vereinen
public static <K, V> Map<K, List<V>> merge(Map<K, List<V>>... maps)

grundlegende Denkweise

Erweitern Sie Map <K, V> [] aufStream <Eintrag <K, V >>und sammeln Sie es. Wenn jedoch mehrere Einträge mit übereinstimmenden Schlüsseln vorhanden sind, müssen Sie von Fall zu Fall darüber nachdenken. Muss sein. Die folgenden zwei Muster können grob unterteilt werden.

--Merge using Collectors # toMap (): Wiederholen Sie die Verarbeitung und Speicherung von Binärwerten mit übereinstimmenden Schlüsseln mit der Merge-Funktion --Merge using Collectors # grounpingBy (): Batch-Prozessschlüssel-übereinstimmende Werte in niedrigeren Collectors

Wenn nur zwei Maps zusammengeführt werden müssen, können Sie dies auch tun. Wir empfehlen daher die Verwendung eines einfachen Collectors # toMap ().

Mit Collectors # toMap () zusammenführen

Ähnlich wie bei Map # merge () kann Collectors # toMap () verwendet werden, wenn die Zusammenführung durch Wiederholen des Vorgangs zum Zusammenführen eines neuen Werts mit einem bereits gespeicherten Wert durchgeführt werden kann.

Map<String, Integer> m1 = new HashMap<>();
m1.put("a", 1);
m1.put("b", 2);
m1.put("c", 3);
Map<String, Integer> m2 = new HashMap<>();
m2.put("a", 100);
m2.put("b", 200);
m2.put("c", 300);

//Wenn die Schlüssel übereinstimmen, nehmen Sie die Summe der Werte
Map<String, Integer> m3 =
    Stream.of(m1, m2)
          .flatMap(m -> m.entrySet().stream())
          .collect(Collectors.toMap(Entry::getKey, Entry::getValue, Integer::sum));

Das dritte Argument von Map # merge () ist BiFunction, während das dritte Argument von Collectors # toMap () BinaryOperator (der Subschnittstelle) ist, das einige Einschränkungen aufweist, aber kein Problem darstellen sollte. Wenn Sie bereits eine BiFunction haben, die Sie an Map # merge () übergeben haben, können Sie sie unverändert anwenden, indem Sie auf BiFunction # apply () verweisen.

// Map#merge()Dritter Argumenttyp von
BiFunction<? super V, ? super V, ? extends V> remappingFunction = ...;

// Collectors#toMap()Umgerechnet auf den Typ des dritten Arguments von
BinaryOperator<V> mergeFunction = remappingFunction::apply;

Mit Collectors zusammenführen # grounpingBy ()

Wenn es 3 oder mehr Werte für denselben Schlüssel gibt und Sie den Durchschnitt davon als zusammengeführten Wert verwenden möchten, verursacht die obige Methode einen Fehler. Da die Schnittstelle BinaryOperator ist, ist es auch nicht möglich, eine Aggregation durchzuführen, bei der der ursprüngliche Wert und der resultierende Wert unterschiedliche Typen haben. In einem solchen Fall müssen Sie Collectors # groupingBy () verwenden, um den Wert für jeden Schlüssel in der Sammlung zu speichern und ihn im Finisher in das Ergebnis zu konvertieren.

//Wenn die Schlüssel übereinstimmen, nehmen Sie den Durchschnitt der Werte
Map<String, Double> m4 =
    Stream.of(m1, m2)
          .flatMap(m -> m.entrySet().stream())
          .collect(Collectors.groupingBy(Entry::getKey, Collectors.averagingInt(Entry::getValue)));

Beispiel für das Zusammenführen von "Map <K, List >"

Als konkreteres Beispiel lassen Sie uns die Daten im Format "Map <K, List >" mit den beiden oben genannten Methoden zusammenführen.

Map<String, List<String>> lm1 = new HashMap<>();
lm1.put("a", Arrays.asList("a1","a2","a3"));
lm1.put("b", Arrays.asList("b1","b2","b3"));
lm1.put("c", Arrays.asList("c1","c2","c3"));
Map<String, List<String>> lm2 = new HashMap<>();
lm2.put("a", Arrays.asList("a3","a4","a5"));
lm2.put("e", Arrays.asList("e1","e2","e3"));
lm2.put("f", Arrays.asList("f1","f2","f3"));

//Funktion zum Zusammenführen von Listen
//Wenn Sie es zu einer Methode machen, können Sie auf die Methode verweisen
BinaryOperator<List<String>> mergeList = (left, right) -> Stream.of(left, right).flatMap(List::stream).collect(Collectors.toList());

//Wenn diese MergeList nur von Collector verwendet wird, ist die folgende Implementierung schneller und spart Speicher.
// BinaryOperator<List<String>> mergeList = (l, r) -> {l.addAll(r); return l;};

// toMap()Verbinden mit
Map<String, List<String>> lm3 =
    Stream.of(lm1, lm2)
          .flatMap(m -> m.entrySet().stream())
          .collect(Collectors.toMap(Entry::getKey, Entry::getValue, mergeList));

// Stream<List<E>>Aufführen<E>Sammler zum Zusammenführen
// Stream<E>Aufführen<E>Sammler zum Aggregieren#toList()Anders als
Collector<List<String>, ?, List<String>> collector = Collector.of(ArrayList::new, List::addAll, mergeList, Characteristics.IDENTITY_FINISH);

// groupingBy()Verbinden mit
Map<String, List<String>> lm4 =
    Stream.of(lm1, lm2)
          .flatMap(m -> m.entrySet().stream())
          .collect(Collectors.groupingBy(Entry::getKey, Collectors.mapping(Entry::getValue, collector)));

Verallgemeinerung

Abschließend werde ich ein Beispiel für eine Verallgemeinerung des Obigen geben und in einer Utility-Klasse zusammengefasst.

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

import java.util.*;
import java.util.Map.Entry;
import java.util.function.*;
import java.util.stream.*;

public class MapMerger {

    // Map<K, V>Geben Sie ein Array von ein<K, V>Zum Streamen erweitern
    @SafeVarargs
    private static <K, V> Stream<Entry<K, V>> flatten(Map<K, V>... maps){
        return Arrays.stream(maps).flatMap(map -> map.entrySet().stream());
    }
    
    //Führen Sie Karten zu verschiedenen Kartentypen zusammen
    @SafeVarargs
    public static <K, V, R> Map<K, R> merge(Collector<V, ?, R> collector, Map<K, V>... maps){
        return flatten(maps).collect(groupingBy(Entry::getKey, mapping(Entry::getValue, collector)));
    }

    //Karte in Karte des gleichen Typs zusammenführen
    @SafeVarargs
    public static <K, V> Map<K, V> merge(BiFunction<? super V, ? super V, ? extends V> mergeFunction, Map<K, V>... maps) {
        return flatten(maps).collect(toMap(Entry::getKey, Entry::getValue, mergeFunction::apply));
        //Wenn Sie die Collector-Version der Implementierung nutzen möchten
//        Function<List<V>, V> finisher = values -> values.stream().reduce(mergeFunction::apply).get();
//        Collector<V, ?, V> collector = Collector.of(ArrayList::new, List::add, MapMerger::mergeIntoList, finisher, Characteristics.IDENTITY_FINISH);
//        return merge(collector, maps);
    }

    //Sammlung in Liste zusammenführen
    @SafeVarargs
    private static <E> List<E> mergeIntoList(Collection<E>... collections){
        return Arrays.stream(collections).flatMap(Collection::stream).collect(toList());
    }
    
    // Map<K, List<V>>Verschmelzen
    @SafeVarargs
    public static <K, V> Map<K, List<V>> merge(Map<K, List<V>>... maps){
        return merge(MapMerger::mergeIntoList, maps);
    }
    
    //Anwendungsbeispiel
    public static void main(String[] args) {
        //Beispieldaten 1
        Map<String, Integer> m1 = new HashMap<>();
        m1.put("a", 1);
        m1.put("b", 2);
        m1.put("c", 3);
        Map<String, Integer> m2 = new HashMap<>();
        m2.put("a", 100);
        m2.put("b", 200);
        m2.put("c", 300);
        
        //Summe
        Map<String, Integer> m3 = merge(Integer::sum, m1, m2);
        Map<String, Integer> m4 = merge(summingInt(Integer::intValue), m1, m2);
        //durchschnittlich
        Map<String, Double> m5 = merge(averagingInt(Integer::intValue), m1, m2);
        
        //Beispieldaten 2
        Map<String, List<String>> lm1 = new HashMap<>();
        lm1.put("a", Arrays.asList("a1","a2","a3"));
        lm1.put("b", Arrays.asList("b1","b2","b3"));
        lm1.put("c", Arrays.asList("c1","c2","c3"));
        Map<String, List<String>> lm2 = new HashMap<>();
        lm2.put("a", Arrays.asList("a3","a4","a5"));
        lm2.put("e", Arrays.asList("e1","e2","e3"));
        lm2.put("f", Arrays.asList("f1","f2","f3"));
        
        //Homogene Verschmelzung
        Map<String, List<String>> lm3 = merge(lm1, lm2);
        Map<String, List<String>> lm4 = merge(MapMerger::mergeIntoList, lm1, lm2);
        
        // Map<String, String>Zusammenführen mit
        Collector<List<String>, ?, String> collector = Collector.<List<String>, List<String>, String>of(
                ArrayList::new,
                List::addAll,
                MapMerger::mergeIntoList,
                List::toString);
        Map<String, String> lm5 = merge(collector, lm1, lm2);
        
        //Ausgabe
        Stream.of(m3, m4, m5).forEach(System.out::println);
        Stream.of(lm3, lm4, lm5).forEach(System.out::println);
    }
}

Recommended Posts

[Java 8+] Karten zusammenführen
Führen Sie Java Word-Dokumente zusammen
Studieren Sie Java-Array, Liste, Karte
Java
Java
Java lernen (0)
Java studieren ―― 3
Java geschützt
[Java] Anmerkung
[Java] Modul
Java-Array
Java studieren ―― 9
Java Scratch Scratch
Java-Tipps, Tipps
Java-Methoden
Java-Methode
Java (Konstruktor)
Java-Array
[Java] ArrayDeque
Java (überschreiben)
Java Day 2018
Java-Zeichenfolge
Java (Array)
Java statisch
Java-Serialisierung
Java Anfänger 4
JAVA hat bezahlt
Java studieren ―― 4
Java (gesetzt)
Java-Shell-Sortierung
Zusammenführungsmethode
[Java] compareTo
Java studieren -5
Java (Schnittstelle)
Java-Memorandum
Java-Array
Java studieren ―― 1
[Java] Array
[Java] Polymorphismus
Java # 0 studieren
Java-Framework
Java-Funktionen
[Java] Vererbung
FastScanner Java
Java-Funktionen
Java Anfänger 3
Java-Memo
Java (Kapselung)
Java-Vererbung
Java-Grundlagen
Java dekompilieren
[Java] Anmerkung
Java Note
Java Anfänger
Java (add2)
JAVA (Karte)
[Java] -Schnittstelle
Java9-Sammlung
Java-Methoden
Java-Tagebuch