Java 8! Filtrando dados com Stream
Com o lançamento do Java 8 vários novos recursos foram adicionados a estrutura da linguagem, para torna-la uma linguagem funcional. Uma linguagem funcional é aquela que o programador não precisa de muitas rotinas para realizar uma operação simples. Neste tutorial será demonstrado um breve exemplo disto, onde teremos uma classe com alguns atributos e vamos realizar uma operação sobre uma lista de objetos usando Stream e Lambda, ambos recursos do Java 8.
Veja no exemplo a seguir a classe Person
, a qual será formada por três atributos, name
, age
e salary
, conforme a Listagem 1.
Listagem 1 – Classe Person
public class Person {
private String name;
private Integer age;
private Double salary;
public Person(String name, int age, Double salary) {
this.name = name;
this.age = age;
this.salary = salary;
}
public String getName() {
return name;
}
public Integer getAge() {
return age;
}
public Double getSalary() {
return salary;
}
@Override
public String toString() {
return "Person{" +
"name='" + name + '\'' +
", age=" + age +
", salary=" + salary +
'}';
}
}
Agora, na classe Main
– Listagem 2 – vamos criar uma lista do tipo Person
e atribuir a cada posição desta lista valores aos atributos via método construtor. No método testeOne()
vamos então realizar um loop na lista e selecionar através de um if
as linhas da lista com age >= 30
. Sendo verdadeiro para este filtro, vamos então somar os valores dos atributos salary
e ao fim do loop exibir a soma no console.
Listagem 2 – Classe Main
package com.mballem.tutorial;
import java.util.ArrayList;
import java.util.List;
public class Main {
public static void main(String[] args) {
List<Person> persons = new ArrayList<>();
persons.add(new Person("Ana Maria", 25, 3200.50));
persons.add(new Person("Bianca", 35, 3440.50));
persons.add(new Person("Marta", 28, 5500.00));
persons.add(new Person("Breno", 22, 3330.70));
persons.add(new Person("Mario", 34, 2200.50));
persons.add(new Person("Ricardo", 55, 3200.00));
persons.add(new Person("Pietra", 26, 3400.50));
persons.add(new Person("Fabiana", 44, 4200.60));
persons.add(new Person("Airton", 29, 1500.50));
testOne(persons);
}
private static void testOne(List<Person> persons) {
Double total = 0.0;
for (Person person : persons) {
if (person.getAge() >= 30) {
total = total + person.getSalary();
}
}
System.out.printf("Sum of Salaries is %.2f", total);
}
}
Como resultado, no console teremos a seguinte mensagem
Sum of Salaries is 13041,60
Analisando o método testOne()
veja que para obter o resultado impresso no console foram necessárias 5 instruções. Com o Java 8, e os recursos de Stream e Lambda, poderemos reduzir isto para apenas 2 instruções veja como na Listagem 3.
Listagem 3 – Método testTwo
private static void testTwo(List<Person> persons) {
Double total = persons.stream()
.filter(p -> p.getAge() >= 30)
.mapToDouble(p -> p.getSalary())
.sum();
System.out.printf("Sum of Salaries is %.2f", total);
}
O método testTwo()
, assim como o método test One()
, recebeu como parâmetro a lista contendo os objetos Person
. Uma variável do tipo Double
é criada para receber o resultado da operação. Esta é a primeira instrução do método. Em uma mesma instrução usamos o método stream()
, provido pela interface java.util.Collection
, que é a superclasse de java.util.List
.
O método stream()
fornece então o método filter()
para adicionarmos uma expressão lambda contendo o nosso filtro, que é o teste de idade. Este teste substitui a necessidade do if
. Para atribuir o valor da soma dos salários, usamos o método mapToDouble()
, já que o retorno será um Double
, também com uma expressão lambda simples.
Por fim, usamos o método sum()
para realizar a soma dos salários, selecionados em mapToDouble()
. Com o valor dos salários atribuídos a variável total, basta criar a segunda instrução, que é o System.out.printf()
.
Sum of Salaries is 13041,60
Basicamente o processo para o uso do Stream é formado da seguinte forma:
- Uma fonte de dados ( a lista );
- Zero ou mais operações intermediarias (
filter()
emapToDouble()
); - Um operação final (
sum()
); - O resultado.
Agora que você já sabe como retornar a soma dos salários por meio de uma filtragem por idade, vamos ver como retornar objetos do tipo Person
para uma lista. Na Listagem 4, vamos criar um filtro que busque na lista original pessoas com o salário maior que 3.400,00. E então, retornar estes objetos selecionados para uma nova lista, e assim, imprimir no console a nova lista.
Listagem 4 – Método testThree
private static void testThree(List<Person> persons) {
List<Person> tempPersons = persons.stream()
.filter((Person p) -> p.getSalary() > 3400)
.collect(Collectors.toList());
tempPersons.forEach(System.out::println);
}
Saída no console:
Person{name='Bianca', age=35, salary=3440.5}
Person{name='Marta', age=28, salary=5500.0}
Person{name='Pietra', age=26, salary=3400.5}
Person{name='Fabiana', age=44, salary=4200.6}
Analisando a saída no console, veja que a lista tempPersons
recebeu os 4 objetos do tipo Person
que possuem salários com valor superior a 3.400,00. Para gerar a nova lista, foi necessário usar o método collect()
, e como parâmetro, passar o método estático toList()
da classe java.util.stream.Collectors
. Desta forma, a operação transforma os objetos selecionados em um retorno do tipo java.util.List
.
Existem mais métodos fornecidos pelo Stream, como para ordenação, contagem, mínimo e máximo, entre outros.
Referencia – https://docs.oracle.com/javase/8/docs/api/java/util/stream/package-summary.html