Filtrando e transformando coleções na goiaba

Filtrando e transformando coleções na goiaba

1. Visão geral

Neste tutorial, ilustraremos comofilter and transform collections with Guava.

Vamos filtrar usando Predicados, transformar usando as funções que a biblioteca oferece e, finalmente, veremos como combinar filtragem e transformação.

Leitura adicional:

Novo stream, comparador e coletor na goiaba 21

Guia rápido e prático de ferramentas no pacote common.collect no Guava 21.

Read more

Guia do Guava Multimap

Um breve guia do Guava Multimap em comparação com o java.util.Map padrão

Read more

Guia de Goiaba RangeSet

Aprenda a usar o Google Guava RangeSet e suas implementações através de exemplos práticos.

Read more

2. Filtrar uma coleção

Vamos começar com um exemplo simples defiltering a collection. Estaremos usando um Predicado pronto para uso fornecido pela biblioteca e construído por meio da classe de utilitárioPredicates:

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

Como você pode ver, estamos filtrandoList dos nomes para obter apenas os nomes que contêm o caractere “a” - e estamos usandoIterables.filter() para fazer isso.

Como alternativa, também podemos fazer bom uso da APICollections2.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());
}

Algumas coisas a serem observadas aqui - primeiro, a saída deCollections.filter() éa live view of the original collection - as alterações em uma serão refletidas na outra.

Também é importante entender que agora,the result is constrained by the predicate - se adicionarmos um elemento que não satisfaz essePredicate, umIllegalArgumentException será lançado:

@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. Gravar filtro personalizadoPredicate

A seguir - vamos escrever nosso próprioPredicate em vez de usar um fornecido pela biblioteca. No exemplo a seguir - definiremos um predicado que obtém apenas os nomes que começam com “A” ou “J”:

@Test
public void whenFilterCollectionWithCustomPredicate_thenFiltered() {
    Predicate predicate = new Predicate() {
        @Override
        public boolean apply(String input) {
            return input.startsWith("A") || input.startsWith("J");
        }
    };

    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. Combine Multiple Predicates

Podemos combinar vários predicados usandoPredicates.or()ePredicates.and(). No exemplo a seguir - filtramos aList de nomes para obter os nomes que começam com “J” ou não contêm “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. Remover valores nulos ao filtrar uma coleção

Podemos limpar os valoresnull de uma coleção, filtrando-os comPredicates.notNull() como no exemplo a seguir:

@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. Verifique se todos os elementos em uma coleção correspondem a uma condição

A seguir, vamos verificar se todos os elementos em uma coleção correspondem a uma determinada condição. UsaremosIterables.all() para verificar se todos os nomes contêm “n” ou “m”, então verificaremos se todos os elementos contêm “a”:

@Test
public void whenCheckingIfAllElementsMatchACondition_thenCorrect() {
    List names = Lists.newArrayList("John", "Jane", "Adam", "Tom");

    boolean result = Iterables.all(names, Predicates.containsPattern("n|m"));
    assertTrue(result);

    result = Iterables.all(names, Predicates.containsPattern("a"));
    assertFalse(result);
}

7. Transforme uma coleção

Agora - vamos ver comotransform a collection using a Guava Function. No exemplo a seguir - transformamosList de nomes emList deIntegers (comprimento do nome) comIterables.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));
}

Também podemos usar a APICollections2.transform() como no exemplo a seguir:

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

Observe que a saída deCollections.transform() éa live view of the original Collection - as alterações de um afetam o outro.

E - o mesmo que antes - se tentarmos adicionar um elemento à saídaCollection, umUnsupportedOperationException será lançado.

8. CrieFunction a partir dePredicate

Também podemos criarFunction a partir dePredicate usandoFunctions.fromPredicate(). Obviamente, essa será uma função que transforma as entradas emBoolean, de acordo com a condição do predicado.

No exemplo a seguir, transformamosList de nomes em uma lista de booleanos em que cada elemento representa se o nome contiver “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. Composição de Duas Funções

A seguir - vamos dar uma olhada em como transformar uma coleção usando umFunction composto.

Functions.compose() retorna a Composição de Duas Funções à medida que aplica o segundoFunction na saída do primeiroFunction.

No exemplo a seguir - o primeiroFunction transforma o nome em seu comprimento, então o segundoFunction transforma o comprimento em um valorboolean que representa se o comprimento do nome é par:

@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. Combine Filtragem e Transformação

E agora - vamos ver outra API legal que o Guava tem - uma que realmente nos permitirá encadear a filtragem e a transformação juntos - oFluentIterable.

No exemplo a seguir - filtramosList dos nomes e depois os transformamos usandoFluentIterable:

@Test
public void whenFilteringAndTransformingCollection_thenCorrect() {
    Predicate predicate = new Predicate() {
        @Override
        public boolean apply(String input) {
            return input.startsWith("A") || input.startsWith("T");
        }
    };

    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 ressaltar que, em alguns casos, a versão imperativa é mais legível e deve ser preferida à abordagem funcional.

11. Conclusão

Por fim, aprendemos a filtrar e transformar coleções usando o Guava. Usamos as APIsCollections2.filter() eIterables.filter() para filtragem, bem comoCollections2.transform()eIterables.transform() para transformar coleções.

Por fim, demos uma olhada rápida na API fluentFluentIterable muito interessante para combinar filtragem e transformação.

A implementação de todos esses exemplos e trechos de códigocan be found in the GitHub project - este é um projeto baseado em Maven, portanto, deve ser fácil de importar e executar como está.