Filtrer et transformer des collections dans Guava

1. Vue d'ensemble

Dans ce didacticiel, nous illustrerons comment filtrer et transformer des collections avec Guava .

Nous filtrerons à l'aide de prédicats, transformerons en utilisant les fonctions fournies par la bibliothèque et enfin, nous verrons comment combiner à la fois filtrage et transformation.

2. Filtrer une collection

Commençons par un exemple simple de filtrage d'une collection . Nous utiliserons un prédicat prêt à l'emploi fourni par la bibliothèque et construit via la classe utilitaire Predicates :

@Test public void whenFilterWithIterables_thenFiltered() { List names = Lists.newArrayList("John", "Jane", "Adam", "Tom"); Iterable result = Iterables.filter(names, Predicates.containsPattern("a")); assertThat(result, containsInAnyOrder("Jane", "Adam")); }

Comme vous pouvez le voir, nous filtrons la liste des noms pour obtenir uniquement les noms qui contiennent le caractère «a» - et nous utilisons Iterables.filter () pour le faire.

Alternativement, nous pouvons également faire bon usage de l' API Collections2.filter () :

@Test public void whenFilterWithCollections2_thenFiltered() { List names = Lists.newArrayList("John", "Jane", "Adam", "Tom"); Collection result = Collections2.filter(names, Predicates.containsPattern("a")); assertEquals(2, result.size()); assertThat(result, containsInAnyOrder("Jane", "Adam")); result.add("anna"); assertEquals(5, names.size()); }

Quelques points à noter ici - d'abord, la sortie de Collections.filter () est une vue en direct de la collection originale - les modifications apportées à l'une seront reflétées dans l'autre.

Il est également important de comprendre que maintenant, le résultat est contraint par le prédicat - si nous ajoutons un élément qui ne satisfait pas ce prédicat , une IllegalArgumentException sera lancée:

@Test(expected = IllegalArgumentException.class) public void givenFilteredCollection_whenAddingInvalidElement_thenException() { List names = Lists.newArrayList("John", "Jane", "Adam", "Tom"); Collection result = Collections2.filter(names, Predicates.containsPattern("a")); result.add("elvis"); }

3. Ecrire un prédicat de filtre personnalisé

Ensuite, écrivons notre propre prédicat au lieu d'utiliser celui fourni par la bibliothèque. Dans l'exemple suivant, nous définirons un prédicat qui n'obtient que les noms commençant par «A» ou «J»:

@Test public void whenFilterCollectionWithCustomPredicate_thenFiltered() { Predicate predicate = new Predicate() { @Override public boolean apply(String input)  return input.startsWith("A")  }; List names = Lists.newArrayList("John", "Jane", "Adam", "Tom"); Collection result = Collections2.filter(names, predicate); assertEquals(3, result.size()); assertThat(result, containsInAnyOrder("John", "Jane", "Adam")); }

4. Combinez plusieurs prédicats

Nous pouvons combiner plusieurs prédicats en utilisant Predicates.or () et Predicates.and () .

Dans l'exemple suivant, nous filtrons une liste de noms pour obtenir les noms commençant par «J» ou ne contenant pas «a»:

@Test public void whenFilterUsingMultiplePredicates_thenFiltered() { List names = Lists.newArrayList("John", "Jane", "Adam", "Tom"); Collection result = Collections2.filter(names, Predicates.or(Predicates.containsPattern("J"), Predicates.not(Predicates.containsPattern("a")))); assertEquals(3, result.size()); assertThat(result, containsInAnyOrder("John", "Jane", "Tom")); }

5. Supprimer les valeurs nulles lors du filtrage d'une collection

Nous pouvons nettoyer les valeurs nulles d'une collection en la filtrant avec Predicates.notNull () comme dans l'exemple suivant:

@Test public void whenRemoveNullFromCollection_thenRemoved() { List names = Lists.newArrayList("John", null, "Jane", null, "Adam", "Tom"); Collection result = Collections2.filter(names, Predicates.notNull()); assertEquals(4, result.size()); assertThat(result, containsInAnyOrder("John", "Jane", "Adam", "Tom")); }

6. Vérifier si tous les éléments d'une collection correspondent à une condition

Ensuite, vérifions si tous les éléments d'une collection correspondent à une certaine condition. Nous utiliserons Iterables.all () pour vérifier si tous les noms contiennent «n» ou «m», puis nous vérifierons si tous les éléments contiennent «a»:

@Test public void whenCheckingIfAllElementsMatchACondition_thenCorrect() m")); assertTrue(result); result = Iterables.all(names, Predicates.containsPattern("a")); assertFalse(result); 

7. Transformer une collection

Voyons maintenant comment transformer une collection à l'aide d'une fonction Guava . Dans l'exemple suivant, nous transformons une liste de noms en une liste d' entiers (longueur du nom) avec Iterables.transform () :

@Test public void whenTransformWithIterables_thenTransformed() { Function function = new Function() { @Override public Integer apply(String input) { return input.length(); } }; List names = Lists.newArrayList("John", "Jane", "Adam", "Tom"); Iterable result = Iterables.transform(names, function); assertThat(result, contains(4, 4, 4, 3)); }

Nous pouvons également utiliser l' API Collections2.transform () comme dans l'exemple suivant:

@Test public void whenTransformWithCollections2_thenTransformed() { Function func = new Function(){ @Override public Integer apply(String input) { return input.length(); } }; List names = Lists.newArrayList("John", "Jane", "Adam", "Tom"); Collection result = Collections2.transform(names, func); assertEquals(4, result.size()); assertThat(result, contains(4, 4, 4, 3)); result.remove(3); assertEquals(3, names.size()); }

Notez que la sortie de Collections.transform () est une vue en direct de la collection d' origine - les modifications apportées à l'une affectent l'autre.

Et - comme avant - si nous essayons d'ajouter un élément à la collection de sortie , une exception UnsupportedOperationException sera lancée.

8. Créer une fonction à partir du prédicat

Nous pouvons également créer une fonction à partir d'un prédicat en utilisant Functions.fromPredicate () . Il s'agit, bien sûr, d'une fonction qui transforme les entrées en booléen , selon la condition du prédicat.

Dans l'exemple suivant, nous transformons une liste de noms en une liste de booléens où chaque élément représente si le nom contient «m»:

@Test public void whenCreatingAFunctionFromAPredicate_thenCorrect() { List names = Lists.newArrayList("John", "Jane", "Adam", "Tom"); Collection result = Collections2.transform(names, Functions.forPredicate(Predicates.containsPattern("m"))); assertEquals(4, result.size()); assertThat(result, contains(false, false, true, true)); }

9. Composition de deux fonctions

Ensuite, voyons comment transformer une collection à l'aide d'une fonction composée .

Functions.compose () renvoie la composition de deux fonctions en appliquant la deuxième fonction à la sortie de la première fonction .

Dans l'exemple suivant, la première fonction transforme le nom en sa longueur, puis la deuxième fonction transforme la longueur en une valeur booléenne qui représente si la longueur du nom est paire:

@Test public void whenTransformingUsingComposedFunction_thenTransformed() { Function f1 = new Function(){ @Override public Integer apply(String input) { return input.length(); } }; Function f2 = new Function(){ @Override public Boolean apply(Integer input) { return input % 2 == 0; } }; List names = Lists.newArrayList("John", "Jane", "Adam", "Tom"); Collection result = Collections2.transform(names, Functions.compose(f2, f1)); assertEquals(4, result.size()); assertThat(result, contains(true, true, true, false)); }

10. Combinez le filtrage et la transformation

And now – let's see another cool API that Guava has – one that will actually allow us to chain filtering and transforming together – the FluentIterable.

In the following example – we filter the List of names then transform it using FluentIterable:

@Test public void whenFilteringAndTransformingCollection_thenCorrect() { Predicate predicate = new Predicate() { @Override public boolean apply(String input)  }; Function func = new Function(){ @Override public Integer apply(String input) { return input.length(); } }; List names = Lists.newArrayList("John", "Jane", "Adam", "Tom"); Collection result = FluentIterable.from(names) .filter(predicate) .transform(func) .toList(); assertEquals(2, result.size()); assertThat(result, containsInAnyOrder(4, 3)); }

It is worth mentioning that, in some cases, the imperative version is more readable and should be preferred to the functional approach.

11. Conclusion

Finally, we learned how to filter and transform collections using Guava. We used the Collections2.filter() and Iterables.filter() APIs for filtering, as well as Collections2.transform() and Iterables.transform() to transform collections.

Enfin, nous avons jeté un coup d'œil à la très intéressante API FluentIterable fluent pour combiner à la fois filtrage et transformation.

La mise en œuvre de tous ces exemples et extraits de code peut être trouvée dans le projet GitHub - il s'agit d'un projet basé sur Maven, il devrait donc être facile à importer et à exécuter tel quel.