[Java 8+] Fusionner les cartes

Choses à faire

Pensez à fusionner plusieurs cartes en une seule carte. Finalement, il sera généralisé aux méthodes utilitaires suivantes.

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

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

// Map<K, List<V>>[]Carte<K, List<V>>Fusionner en
public static <K, V> Map<K, List<V>> merge(Map<K, List<V>>... maps)

façon de penser de base

Développez Map <K, V> [] to` Stream <Entry <K, V >> ʻet collectez-le, mais s'il y a plusieurs entrées avec des clés correspondantes, vous devez y penser au cas par cas. Doit être. Les deux modèles suivants peuvent être grossièrement divisés.

--Merge using Collectors # toMap (): Répétez le traitement et le stockage des valeurs binaires avec les clés correspondantes avec la fonction de fusion --Fusion à l'aide de collecteurs # grounpingBy (): traitement par lots des valeurs de correspondance de clé dans les collecteurs inférieurs

S'il n'y a que deux cartes à fusionner, vous pouvez toujours le faire, nous vous recommandons donc d'utiliser un simple Collectors # toMap ().

Fusionner à l'aide de Collectors # toMap ()

Similaire à Map # merge (), Collectors # toMap () peut être utilisé si vous pouvez fusionner en répétant l'opération de fusion d'une nouvelle valeur avec une valeur déjà stockée.

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);

//Si les clés correspondent, prenez la somme des valeurs
Map<String, Integer> m3 =
    Stream.of(m1, m2)
          .flatMap(m -> m.entrySet().stream())
          .collect(Collectors.toMap(Entry::getKey, Entry::getValue, Integer::sum));

Le troisième argument de Map # merge () est BiFunction, tandis que le troisième argument de Collectors # toMap () est BinaryOperator (de la sous-interface), qui a quelques restrictions, mais cela ne devrait pas être un problème. Si vous avez déjà une fonction BiFunction que vous avez passée à Map # merge (), vous pouvez l'appliquer telle quelle en vous référant à BiFunction # apply ().

// Map#merge()Troisième type d'argument de
BiFunction<? super V, ? super V, ? extends V> remappingFunction = ...;

// Collectors#toMap()Converti au type du troisième argument de
BinaryOperator<V> mergeFunction = remappingFunction::apply;

Fusionner à l'aide de Collectors # grounpingBy ()

S'il existe 3 valeurs ou plus pour la même clé et que vous souhaitez utiliser la moyenne de celles-ci comme valeur fusionnée, la méthode ci-dessus provoquera une erreur. De plus, étant donné que l'interface est BinaryOperator, il n'est pas possible d'effectuer une agrégation dans laquelle la valeur d'origine et la valeur résultante ont des types différents. Dans ce cas, vous devez utiliser Collectors # groupingBy () pour stocker la valeur de chaque clé de la collection et la convertir en résultat au niveau du module de finition.

//Si les clés correspondent, prenez la moyenne des valeurs
Map<String, Double> m4 =
    Stream.of(m1, m2)
          .flatMap(m -> m.entrySet().stream())
          .collect(Collectors.groupingBy(Entry::getKey, Collectors.averagingInt(Entry::getValue)));

Exemple de fusion de Map <K, List <V >>

Comme exemple plus concret, fusionnons les données au format Map <K, List <V >> par les deux méthodes ci-dessus.

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"));

//Fonction pour fusionner des listes
//Si vous en faites une méthode, vous pouvez vous référer à la méthode
BinaryOperator<List<String>> mergeList = (left, right) -> Stream.of(left, right).flatMap(List::stream).collect(Collectors.toList());

//Si cette mergeList est utilisée uniquement par Collector, l'implémentation suivante sera plus rapide et économisera de la mémoire.
// BinaryOperator<List<String>> mergeList = (l, r) -> {l.addAll(r); return l;};

// toMap()Fusionner avec
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>>liste<E>Collecteur à fusionner
// Stream<E>liste<E>Collectionneurs à regrouper#toList()Différent de
Collector<List<String>, ?, List<String>> collector = Collector.of(ArrayList::new, List::addAll, mergeList, Characteristics.IDENTITY_FINISH);

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

Généralisation

Enfin, je donnerai un exemple de version généralisée de ce qui précède et résumée dans une classe d'utilité.

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>Entrez un tableau de<K, V>Développer pour diffuser
    @SafeVarargs
    private static <K, V> Stream<Entry<K, V>> flatten(Map<K, V>... maps){
        return Arrays.stream(maps).flatMap(map -> map.entrySet().stream());
    }
    
    //Fusionner les cartes dans différents types de cartes
    @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)));
    }

    //Fusionner la carte dans la carte du même type
    @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));
        //Si vous souhaitez profiter de la version collector de l'implémentation
//        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);
    }

    //Fusionner la collection dans la liste
    @SafeVarargs
    private static <E> List<E> mergeIntoList(Collection<E>... collections){
        return Arrays.stream(collections).flatMap(Collection::stream).collect(toList());
    }
    
    // Map<K, List<V>>Fusionner
    @SafeVarargs
    public static <K, V> Map<K, List<V>> merge(Map<K, List<V>>... maps){
        return merge(MapMerger::mergeIntoList, maps);
    }
    
    //Exemple d'utilisation
    public static void main(String[] args) {
        //Exemple de données 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);
        
        //somme
        Map<String, Integer> m3 = merge(Integer::sum, m1, m2);
        Map<String, Integer> m4 = merge(summingInt(Integer::intValue), m1, m2);
        //moyenne
        Map<String, Double> m5 = merge(averagingInt(Integer::intValue), m1, m2);
        
        //Exemple de données 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"));
        
        //Fusion homogène
        Map<String, List<String>> lm3 = merge(lm1, lm2);
        Map<String, List<String>> lm4 = merge(MapMerger::mergeIntoList, lm1, lm2);
        
        // Map<String, String>Fusionner avec
        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);
        
        //production
        Stream.of(m3, m4, m5).forEach(System.out::println);
        Stream.of(lm3, lm4, lm5).forEach(System.out::println);
    }
}

Recommended Posts

[Java 8+] Fusionner les cartes
Fusionner des documents Java Word
Étudier le tableau, la liste, la carte Java
Java
Java
Apprendre Java (0)
Étudier Java ―― 3
Java protégé
[Java] Annotation
Module [Java]
Tableau Java
Étudier Java ―― 9
Java scratch scratch
Astuces Java, astuces
Méthodes Java
Méthode Java
java (constructeur)
Tableau Java
[Java] ArrayDeque
java (remplacement)
Journée Java 2018
Chaîne Java
java (tableau)
Java statique
Sérialisation Java
java débutant 4
JAVA payé
Étudier Java ―― 4
Java (ensemble)
tri shell java
méthode de fusion
[Java] compareTo
Étudier Java -5
java (interface)
Mémorandum Java
Tableau Java
Étudier Java ―― 1
[Java] Array
[Java] Polymorphisme
Étudier Java # 0
framework java
Fonctionnalités Java
[Java] Héritage
FastScanner Java
Fonctionnalités Java
java débutant 3
Mémo Java
java (encapsulation)
Héritage Java
Les bases de Java
Décompiler Java
[Java] Annotation
note java
java débutant
Java (add2)
JAVA (Carte)
interface [java]
Collection Java9
Méthodes Java
Journal de Java