Fusion de deux cartes avec Java 8

Fusion de deux cartes avec Java 8

 

1. introduction

Dans ce tutoriel rapide,we’ll demonstrate how to merge two maps using the Java 8 capabilities.

Pour être plus précis, nous examinerons différents scénarios de fusion, y compris les cartes comportant des entrées en double.

2. Initialisation

Pour commencer, définissons deux instancesMap:

private static Map map1 = new HashMap<>();
private static Map map2 = new HashMap<>();

La classeEmployee ressemble à ceci:

public class Employee {
 
    private Long id;
    private String name;
 
    // constructor, getters, setters
}

Ensuite, nous pouvons pousser certaines données dans les instancesMap:

Employee employee1 = new Employee(1L, "Henry");
map1.put(employee1.getName(), employee1);
Employee employee2 = new Employee(22L, "Annie");
map1.put(employee2.getName(), employee2);
Employee employee3 = new Employee(8L, "John");
map1.put(employee3.getName(), employee3);

Employee employee4 = new Employee(2L, "George");
map2.put(employee4.getName(), employee4);
Employee employee5 = new Employee(3L, "Henry");
map2.put(employee5.getName(), employee5);

Notez que nous avons des clés identiques pour les entréesemployee1 etemployee5 dans nos cartes que nous utiliserons plus tard.

3. Map.merge()

Java 8 adds a new merge() function into the java.util.Map interface.

Voici comment fonctionne la fonctionmerge(): Si la clé spécifiée n'est pas déjà associée à une valeur ou si la valeur est nulle, elle associe la clé à la valeur donnée.

Sinon, il remplace la valeur par les résultats de la fonction de remappage donnée. Si le résultat de la fonction de remappage est null, il supprime le résultat.

Tout d'abord, construisons un nouveauHashMap en copiant toutes les entrées desmap1:

Map map3 = new HashMap<>(map1);

Ensuite, introduisons la fonctionmerge() avec la règle de fusion:

map3.merge(key, value, (v1, v2) -> new Employee(v1.getId(),v2.getName())

Enfin, nous allons parcourir lesmap2 et fusionner les entrées dansmap3:

map2.forEach(
  (key, value) -> map3.merge(key, value, (v1, v2) -> new Employee(v1.getId(),v2.getName())));

Exécutons le programme et imprimons le contenu demap3:

John=Employee{id=8, name='John'}
Annie=Employee{id=22, name='Annie'}
George=Employee{id=2, name='George'}
Henry=Employee{id=1, name='Henry'}

En conséquence,our combined Map has all the elements of the previous HashMap entries. Entries with duplicate keys have been merged into one entry.

De plus, nous remarquons que l'objetEmployee de la dernière entrée a lesid desmap1 et que la valeur est choisie parmimap2.

Ceci est dû à la règle que nous avons définie dans notre fonction de fusion:

(v1, v2) -> new Employee(v1.getId(), v2.getName())

4. Stream.concat()

L'APIStream de Java 8 peut également fournir une solution simple à notre problème. Tout d'abord,we need to combine our Map instances into one Stream. C'est exactement ce que fait l'opérationStream.concat():

Stream combined = Stream.concat(map1.entrySet().stream(), map2.entrySet().stream());

Nous passons ici les ensembles d’entrée de la carte en tant que paramètres. Next, we need to collect our result into a new Map. Pour cela, nous pouvons utiliserCollectors.toMap():

Map result = combined.collect(
  Collectors.toMap(Map.Entry::getKey, Map.Entry::getValue));

En conséquence, le collecteur utilisera les clés et les valeurs existantes de nos cartes. Mais cette solution est loin d’être parfaite. Dès que notre collectionneur rencontre des entrées avec des clés en double, il lancera unIllegalStateException.

Pour gérer ce problème, nous ajoutons simplement un troisième paramètre lambda «fusion» dans notre collecteur:

(value1, value2) -> new Employee(value2.getId(), value1.getName())

Il utilisera l'expression lambda chaque fois qu'une clé en double est détectée.

Enfin, en mettant tous ensemble:

Map result = Stream.concat(map1.entrySet().stream(), map2.entrySet().stream())
  .collect(Collectors.toMap(
    Map.Entry::getKey,
    Map.Entry::getValue,
    (value1, value2) -> new Employee(value2.getId(), value1.getName())));

Enfin, exécutons le code et voyons les résultats:

George=Employee{id=2, name='George'}
John=Employee{id=8, name='John'}
Annie=Employee{id=22, name='Annie'}
Henry=Employee{id=3, name='Henry'}

Comme nous le voyons, les entrées en double avec la clé“Henry” ont été fusionnées dans une nouvelle paire clé-valeur oùthe id of the new Employee was picked from the map2 and the value from map1.

5. Stream.of()

Pour continuer à utiliser l'APIStream, nous pouvons transformer nos instancesMap en un flux unifié à l'aide deStream.of().

Ici, nous n'avons pas besoin de créer une collection supplémentaire pour travailler avec les flux:

Map map3 = Stream.of(map1, map2)
  .flatMap(map -> map.entrySet().stream())
  .collect(Collectors.toMap(
    Map.Entry::getKey,
    Map.Entry::getValue,
    (v1, v2) -> new Employee(v1.getId(), v2.getName())));

Tout d'abord,we transform map1 and map2 into a single stream. Ensuite, nous convertissons le flux sur la carte. Comme nous pouvons le voir, le dernier argument detoMap() est une fonction de fusion. Il résout le problème des clés en double en choisissant le champ id dans l'entréev1 et le nom dansv2.

L'instancemap3 imprimée après l'exécution du programme:

George=Employee{id=2, name='George'}
John=Employee{id=8, name='John'}
Annie=Employee{id=22, name='Annie'}
Henry=Employee{id=1, name='Henry'}

6. Streaming simple

De plus, nous pouvons utiliser une spipelinestream() pour assembler nos entrées de carte. L'extrait de code ci-dessous montre comment ajouter les entrées demap2 etmap1 en ignorant les entrées en double:

Map map3 = map2.entrySet()
  .stream()
  .collect(Collectors.toMap(
    Map.Entry::getKey,
    Map.Entry::getValue,
    (v1, v2) -> new Employee(v1.getId(), v2.getName()),
  () -> new HashMap<>(map1)));

Comme nous nous y attendons, les résultats après la fusion sont les suivants:

{John=Employee{id=8, name='John'},
Annie=Employee{id=22, name='Annie'},
George=Employee{id=2, name='George'},
Henry=Employee{id=1, name='Henry'}}

7. StreamEx

En plus des solutions fournies par le JDK, nous pouvons également utiliser la célèbre slibraryStreamEx .

En termes simples,StreamEx is an enhancement for the Stream API et fournit de nombreuses méthodes utiles supplémentaires. Nous utiliserons unEntryStream instance to operate on key-value pairs:

Map map3 = EntryStream.of(map1)
  .append(EntryStream.of(map2))
  .toMap((e1, e2) -> e1);

L'idée est de fusionner les flux de nos cartes en un seul. Ensuite, nous collectons les entrées dans la nouvelle instance demap3. Il est important de mentionner, l'expression(e1, e2) → e1 car elle aide à définir la règle pour traiter les clés en double. Sans cela, notre code lancera unIllegalStateException.

Et maintenant, les résultats:

{George=Employee{id=2, name='George'},
John=Employee{id=8, name='John'},
Annie=Employee{id=22, name='Annie'},
Henry=Employee{id=1, name='Henry'}}

8. Sommaire

Dans ce court article, nous avons appris différentes manières de fusionner des cartes dans Java 8. Plus précisément,we used Map.merge(), Stream API, StreamEx library.

Comme toujours, le code utilisé lors de la discussion se trouveover on GitHub.