Travailler avec des cartes à l'aide de flux

1. Introduction

Dans ce didacticiel, nous discuterons de quelques exemples d'utilisation de Java Stream spour travailler avec Map s. Il est à noter que certains de ces exercices pourraient être résolus en utilisant une structure de données bidirectionnelle Map , mais nous nous intéressons ici à une approche fonctionnelle.

Tout d'abord, nous expliquons l'idée de base que nous utiliserons pour travailler avec Maps et Stream s. Ensuite, nous présentons quelques problèmes différents liés à Maps et leurs solutions concrètes à l'aide de Stream s.

2. Idée de base

La principale chose à noter est que les Stream sont des séquences d'éléments qui peuvent être facilement obtenues à partir d'une Collection .

Les cartes ont une structure différente, avec un mappage des clés aux valeurs, sans séquence. Cela ne signifie pas que nous ne pouvons pas convertir une structure Map en différentes séquences qui nous permettent ensuite de travailler de manière naturelle avec l'API Stream.

Voyons comment obtenir différentes collections à partir d'une carte , que nous pouvons ensuite faire pivoter dans un flux :

Map someMap = new HashMap();

Nous pouvons obtenir un ensemble de paires clé-valeur:

Set
    
      entries = someMap.entrySet();
    

Nous pouvons également obtenir le jeu de clés associé à la carte :

Set keySet = someMap.keySet();

Ou nous pourrions travailler directement avec l'ensemble de valeurs:

Collection values = someMap.values();

Celles-ci nous donnent chacune un point d'entrée pour traiter ces collections en obtenant des flux à partir d'elles:

Stream
    
      entriesStream = entries.stream(); Stream valuesStream = values.stream(); Stream keysStream = keySet.stream();
    

3. Obtenir une carte de » clés en utilisant flux s

3.1. Des données d'entrée

Supposons que nous ayons une carte :

Map books = new HashMap(); books.put( "978-0201633610", "Design patterns : elements of reusable object-oriented software"); books.put( "978-1617291999", "Java 8 in Action: Lambdas, Streams, and functional-style programming"); books.put("978-0134685991", "Effective Java");

Nous sommes intéressés à trouver l'ISBN du livre intitulé «Effective Java».

3.2. Récupérer une correspondance

Le titre du livre ne pouvant pas exister dans notre carte , nous souhaitons pouvoir indiquer qu'il n'y a pas d'ISBN associé. Nous pouvons utiliser un facultatif pour exprimer que :

Supposons pour cet exemple que nous sommes intéressés par n'importe quelle clé pour un livre correspondant à ce titre:

Optional optionalIsbn = books.entrySet().stream() .filter(e -> "Effective Java".equals(e.getValue())) .map(Map.Entry::getKey) .findFirst(); assertEquals("978-0134685991", optionalIsbn.get());

Analysons le code. Tout d'abord, nous obtenons le entrySet à partir de la carte , comme nous l'avons vu précédemment.

Nous ne voulons considérer que les entrées avec «Effective Java» comme titre, donc la première opération intermédiaire sera un filtre.

Nous ne sommes pas intéressés par toute l' entrée de la carte , mais par la clé de chaque entrée. Donc, la prochaine opération intermédiaire chaînée fait exactement cela: c'est une opération de carte qui générera un nouveau flux en sortie qui ne contiendra que les clés pour les entrées qui correspondent au titre recherché.

Comme nous ne voulons qu'un seul résultat, nous pouvons appliquer l' opération de terminal findFirst () , qui fournira la valeur initiale dans le Stream en tant qu'objet facultatif .

Voyons un cas dans lequel un titre n'existe pas:

Optional optionalIsbn = books.entrySet().stream() .filter(e -> "Non Existent Title".equals(e.getValue())) .map(Map.Entry::getKey).findFirst(); assertEquals(false, optionalIsbn.isPresent());

3.3. Récupération de plusieurs résultats

Modifions le problème maintenant pour voir comment nous pourrions gérer le renvoi de plusieurs résultats au lieu d'un.

Pour avoir plusieurs résultats renvoyés, ajoutons le livre suivant à notre carte :

books.put("978-0321356680", "Effective Java: Second Edition"); 

Alors maintenant, si nous recherchons tous les livres commençant par «Effective Java», nous obtiendrons plus d'un résultat:

List isbnCodes = books.entrySet().stream() .filter(e -> e.getValue().startsWith("Effective Java")) .map(Map.Entry::getKey) .collect(Collectors.toList()); assertTrue(isbnCodes.contains("978-0321356680")); assertTrue(isbnCodes.contains("978-0134685991"));

Ce que nous avons fait dans ce cas est de remplacer la condition de filtre pour vérifier si la valeur de la carte commence par «Effective Java» au lieu de comparer pour l' égalité de chaîne .

Cette fois, nous collectons les résultats - au lieu de choisir le premier - en mettant les correspondances dans une liste .

4. Obtenir une carte les valeurs de l' aide de flux s

Maintenant, concentrons-nous sur un problème différent avec les cartes: au lieu d'obtenir des ISBN basés sur les titres , nous allons essayer d'obtenir des titres basés sur les ISBN.

Utilisons la carte originale . Nous recherchons des titres dont l'ISBN commence par «978-0».

List titles = books.entrySet().stream() .filter(e -> e.getKey().startsWith("978-0")) .map(Map.Entry::getValue) .collect(Collectors.toList()); assertEquals(2, titles.size()); assertTrue(titles.contains( "Design patterns : elements of reusable object-oriented software")); assertTrue(titles.contains("Effective Java"));

Cette solution est similaire aux solutions de notre précédent ensemble de problèmes - nous diffusons l'ensemble d'entrées, puis filtrons, mappons et collectons.

Et comme avant, si nous voulions ne renvoyer que la première correspondance, nous pourrions après la méthode map appeler la méthode findFirst () au lieu de collecter tous les résultats dans une liste .

5. Conclusion

Nous avons montré comment traiter une carte de manière fonctionnelle .

En particulier, nous avons vu qu'une fois que nous passons à l'utilisation des collections associées vers les Map s, le traitement à l'aide de Stream s devient beaucoup plus simple et intuitif.

Et, bien sûr, tous les exemples se trouvent dans le projet GitHub.