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.
Guia do Guava Multimap
Um breve guia do Guava Multimap em comparação com o java.util.Map padrão
Guia de Goiaba RangeSet
Aprenda a usar o Google Guava RangeSet e suas implementações através de exemplos práticos.
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á.