Filtraggio e trasformazione di raccolte in Guava

1. Panoramica

In questo tutorial, illustreremo come filtrare e trasformare le raccolte con Guava .

Filtreremo usando Predicates, trasformeremo usando le funzioni fornite dalla libreria e, infine, vedremo come combinare sia il filtraggio che la trasformazione.

2. Filtrare una raccolta

Cominciamo con un semplice esempio di filtraggio di una raccolta . Useremo un Predicate pronto all'uso fornito dalla libreria e costruito tramite la classe di utilità 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")); }

Come puoi vedere, stiamo filtrando la Lista dei nomi per ottenere solo i nomi che contengono il carattere "a" - e stiamo usando Iterables.filter () per farlo.

In alternativa, possiamo anche fare buon uso dell'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()); }

Alcune cose da notare qui - in primo luogo, l'output di Collections.filter () è una visualizzazione live della raccolta originale - le modifiche a una si rifletteranno nell'altra.

È anche importante capire che ora il risultato è vincolato dal predicato : se aggiungiamo un elemento che non soddisfa quel Predicate , verrà generata un'eccezione IllegalArgumentException :

@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. Scrivi predicato filtro personalizzato

Avanti: scriviamo il nostro predicato invece di usarne uno fornito dalla libreria. Nell'esempio seguente, definiremo un predicato che ottiene solo i nomi che iniziano con "A" o "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. Combina più predicati

Possiamo combinare più predicati utilizzando Predicates.or () e Predicates.and () .

Nell'esempio seguente, filtriamo un elenco di nomi per ottenere i nomi che iniziano con "J" o non contengono "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. Rimuovere i valori nulli durante il filtraggio di una raccolta

Possiamo ripulire i valori null da una raccolta filtrandola con Predicates.notNull () come nell'esempio seguente:

@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. Controllare se tutti gli elementi in una raccolta corrispondono a una condizione

Successivamente, controlliamo se tutti gli elementi in una raccolta corrispondono a una determinata condizione. Useremo Iterables.all () per verificare se tutti i nomi contengono "n" o "m", quindi controlleremo se tutti gli elementi contengono "a":

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

7. Trasforma una raccolta

Ora vediamo come trasformare una raccolta utilizzando una funzione Guava . Nell'esempio seguente, trasformiamo un elenco di nomi in un elenco di numeri interi (lunghezza del nome) con 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)); }

Possiamo anche utilizzare l' API Collections2.transform () come nell'esempio seguente:

@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()); }

Notare che l'output di Collections.transform () è una visualizzazione live della Collection originale : le modifiche a una hanno effetto sull'altra.

E, come prima, se proviamo ad aggiungere un elemento all'output Collection , verrà generata un'eccezione UnsupportedOperationException .

8. Crea funzione da Predicate

Possiamo anche creare una funzione da un predicato usando Functions.fromPredicate () . Questa, ovviamente, sarà una funzione che trasforma gli input in booleani , in base alla condizione del predicato.

Nell'esempio seguente, trasformiamo un elenco di nomi in un elenco di valori booleani in cui ogni elemento rappresenta se il nome contiene "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. Composizione di due funzioni

Successivamente, diamo un'occhiata a come trasformare una raccolta utilizzando una funzione composta .

Functions.compose () restituisce la composizione di due funzioni poiché applica la seconda funzione all'output della prima funzione .

Nell'esempio seguente, la prima funzione trasforma il nome nella sua lunghezza, quindi la seconda funzione trasforma la lunghezza in un valore booleano che rappresenta se la lunghezza del nome è pari:

@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. Combina filtri e trasformazioni

E ora, vediamo un'altra fantastica API che ha Guava, una che ci consentirà effettivamente di concatenare filtri e trasformazioni insieme: FluentIterable .

Nell'esempio seguente, filtriamo l' elenco dei nomi, quindi lo trasformiamo utilizzando 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)); }

Vale la pena ricordare che, in alcuni casi, la versione imperativa è più leggibile e dovrebbe essere preferita all'approccio funzionale.

11. Conclusione

Infine, abbiamo imparato a filtrare e trasformare le collezioni usando Guava. Abbiamo utilizzato le API Collections2.filter () e Iterables.filter () per il filtraggio, così come Collections2.transform () e Iterables.transform () per trasformare le raccolte.

Infine, abbiamo dato una rapida occhiata all'interessante API FluentIterable fluent per combinare filtri e trasformazioni.

L'implementazione di tutti questi esempi e frammenti di codice può essere trovata nel progetto GitHub : questo è un progetto basato su Maven, quindi dovrebbe essere facile da importare ed eseguire così com'è.