- JP
Java: Streams API - Filter
Desde o Java 8 lançado em 2014 foram adicionados dezenas de novas funcionalidades dentre melhorias na JVM e funções para facilitar a vida do desenvolvedor, pois ele merece. Dentre estas features, estão as Expression Lambda (EL) que foi o ponta pé inicial para a entrada do Java no mundo da programação funcional, melhoria na API de data e a não necessidade de criar implementações de Interfaces já existentes com a utilização de Default methods.
E a outra novidade é a API de Streams, o foco desse post. A Stream API é uma nova abordagem para se trabalhar com Collections deixando o código menos verboso e mais inteligente.
A Stream API trabalha com o processamento de dados sob demanda e fornece dezenas de funcionalidades para manipular Collections diminuindo o código e simplificando o desenvolvimento em uma espécie de pipeline que será explicado mais a frente.
Para entender melhor, vamos criar um código simples de uma lista de objetos que será percorrida e nela vamos criar algumas condições afim de extrair uma nova lista com os valores desejados. Neste exemplo não utilizaremos Streams API.
Vamos criar um Classe representando a entidade Cidade no qual terá como atributos: nome, estado e população. E por fim um método chamado listaCidades que carrega uma lista de objetos do tipo Cidade.
public class Cidade {
String nome;
String estado;
long populacao;
public Cidade(){}
public Cidade(String nome, String estado,
long populacao){
this.nome = nome;
this.estado = estado;
this.populacao = populacao;
}
public List<Cidade> listaCidades(){
List<Cidade> cidades = new ArrayList<Cidade>();
cidades.add(new Cidade("Hollywood", "CA", 30L));
cidades.add(new Cidade("Venice", "CA", 10L));
cidades.add(new Cidade("Houston", "TX", 14L));
cidades.add(new Cidade("New York", "NY", 21L));
cidades.add(new Cidade("Albany", "NY", 11L));
cidades.add(new Cidade("Rio de Janeiro", "RJ", 14L));
cidades.add(new Cidade("São Paulo", "SP", 90L));
return cidades;
}
@Override
public String toString() {
return "Cidade: " + nome +
" /Estado: " + estado +
" /População: " + populacao;
}
}
Filter
No próximo código, vamos percorrer uma lista, e dentro da iteração será verificado quais cidades possuem uma população maior do que 20 e em seguida adicionada em uma lista secundária.
public static void main(String[] args) {
Cidade cidade = new Cidade();
List<Cidade> cidadesComPopulacaoMaiorQue20
= new ArrayList<Cidade>();
for(Cidade c : cidade.listaCidades()){
if(c.populacao > 20L){
cidadesComPopulacaoMaiorQue20.add(c);
}
}
}
Até então, tecnicamente não existe problemas com o código acima. Mas poderia ser menos verboso e mais prático. Agora, utilizando Stream API, segue um novo exemplo.
public static void main(String[] args) {
Cidade cidade = new Cidade();
List<Cidade> cidadesComPopulacaoMaiorQue20 =
new ArrayList<Cidade>();
cidadesComPopulacaoMaiorQue20 =
cidade.listaCidades()
.stream()
.filter(c -> c.populacao > 20L)
.collect(Collectors.toList());
}
Perceba a diferença, invocamos o método listaCidades e por este retornar uma Collection, é possível invocar o método Stream. A partir da chamada do método stream() se dá início a pipeline.
Mais exemplos de Filter
Exemplo 1:
cidadesComPopulacaoMaiorQue20 =
cidade.listaCidades()
.stream()
.filter(c -> c.estado.equals("CA"))
.collect(Collectors.toList());
cidadesComPopulacaoMaiorQue20.forEach(
c -> System.out.println(c)
);
Saída:
Cidade: Hollywood /Estado: CA /População: 30
Cidade: Venice /Estado: CA /População: 10
Exemplo 2:
cidadesComPopulacaoMaiorQue20 =
cidade.listaCidades()
.stream()
.filter(c -> c.estado.equals("CA") &&
c.populacao < 30)
.collect(Collectors.toList());
cidadesComPopulacaoMaiorQue20.forEach(
c -> System.out.println(c)
);
Saída:
Cidade: Venice /Estado: CA /População: 10
Como funciona a pipeline?
Seguindo o exemplo anterior, a pipeline é um processo sequencial que se diferencia entre operações intermediárias e finais. No exemplo, a Stream é invocada a partir de uma fonte de dados (lista de objetos do tipo Cidade) que trabalha sob demanda, o método filter é uma operação intermediária, ou seja, ela processa os dados até que o método collect é invocado, originando uma operação final.
E aí, Curtiu? Até mais!