Java Streams
Como motivação para meu primeiro post aqui no TabNews decidi mostrar um pouco sobre a manipulação de listas utilizando a API Java Streams.
Sempre senti dificuldade em entender essa api e passei anos como dev java sem utilizar esse recurso, mas nos últimos anos venho me aperfeiçoando em JavaScript e nele tem funções como .map
, .find
, .reduce
entre outras, e senti um ganho de produtividade imensa, foi daí que decidi ter o mesmo ganho em java.
Java Streams é uma API inicialmente introduzida no Java 8 e vem tendo atualizações interessantes para melhorar seu uso.
Java Streams tem como princípio fazer a manipulação de Collections
de forma mais simples juntamente alinhada com uma programação mais funcional
utilizando as functions lambda
.
Lembrete: Se você não sabe como fazer com for normal
talvez streams não seja para você, então cuidado
ao utilizar essa API poderosa;
Com grandes poderes vem grandes responsabilidade ~ Ben Parker
Mais sobre: https://www.devmedia.com.br/java-streams-api-manipulando-colecoes-de-forma-eficiente/37630
Documentação oficial:
Vou mostrar alguns exemplos lidando com listas no modo tradicional e com streams
Para os exemplos a seguir vamos assumir que temos a seguinte lista
private static List<Person> personList = new ArrayList<Person>();
personList.add(new Person(1, "Bob", 'M', 25));
personList.add(new Person(2, "Alice", 'F', 18));
personList.add(new Person(3, "John", 'M', 20));
personList.add(new Person(4, "Michael", 'M', 32));
Foreach
Como você pode ver a baixo não há muita diferença, (Eu particularmente prefiro no modo normal ainda) mas prometo que nos próximos exemplos terá maior impacto.
- Normal
for(Person p: personList) {
System.out.println(p.getName());
}
- Lambda
personList.stream().forEach(p -> {
System.out.println(p.getName());
});
Map
Para o map
temos como objetivo criar uma lista com apenas os nomes
- Normal
List<String> names = new ArrayList<String>();
for (Person p : personList) {
names.add(p.getName());
}
- Lambda
List<String> names = personList.stream()
.map(Person::getName)
.collect(Collectors.toList());
Filter
Objetivo e obter uma lista com todas as person do sexo masculino
- Normal
List<Person> personTempList = new ArrayList<Person>();
for (Person p : personList) {
char sex = p.getSex();
if('M' == sex) {
personTempList.add(p);
}
}
- Lambda
List<Person> personTempList = personList.stream()
.filter(p -> 'M' == p.getSex())
.collect(Collectors.toList());
Bem esses são apenas alguns exemplos para o post, mais exemplos de código você encontra no meu Git.
Todo código-fonte está no meu Git
Antes
List<Person> personTempList = personList.stream()
.filter(p -> 'M' == p.getSex())
.collect(Collectors.toList());
Depois
public static boolean hasSexMasculine(Person person) {
return person.getSex() == 'M';
}
List<Person> personTempList = personList.stream()
.filter(Person::hasSexMasculine)
.collect(Collectors.toList())
Esta mudança pode até parecer entranha ou que esta deixando o código mais sujo, mas bem pelo contrário com uma função como essa pode ocasionar, mesmo erros de escritas aumentando a desempenho de sua equipe.
Muito bom man, parabéns. No começo eu achava streams um verdadeiro bixo de 777 cabeças. Mas conhecendo melhor a API percebi que é a coisa mais simples e mais agradavel de trabalhar que existe kkk.
Deixando minha humilde contribuição, além dos métodos que vc destacou, eu também uso muito no dia a dia como dev os métodos anyMatch
, noneMatch
e allMatch
, eles são bem legais, ajudam a encontrar determinadas ocorrências numa lista de forma bem simples, por exemplo:
1. anyMatch:
- Retorna verdadeiro se pelo menos um item da lista satisfizer a condição, no caso abaixo, retornará verdadeiro se pelo menos uma das pessoas for maior de idade.
personList.stream().anyMatch(p -> p.age >= 18);
2. noneMatch:
- Retorna verdadeiro se ninguém da lista satisfizer a condição, no caso abaixo, retornará verdadeiro se nenhuma das pessoas for maior de idade.
personList.stream().noneMatch(p -> p.age >= 18);
3. allMatch:
- Retorna verdadeiro se todos os itens da lista satisfizerem a condição, no caso abaixo, retornará verdadeiro se todas as pessoas forem maiores de idade.
personList.stream().allMatch(p -> p.age >= 18);
O mais massa é que a JVM já otimiza esses métodos, todos eles param assim que é possível definir o resultado, por exemplo, o método anyMatch
para
de executar no momento
em que ele encontra o primeiro caso que da match
(retorna verdadeiro). O método noneMatch
para
de executar no momento
em que ele encontra o primeiro caso que dê match
(e retorna falso). E o método allMatch
para
de executar no momento
em que ele encontra o primeiro caso que não dê match
(e retorna falso). O código fica mais legível (e por consequência, agradável de ler e entender) do que se isso fosse feito na mão com for
e if
.
Oi Deivid! :D
Me perdoe por fugir um pouco do escopo do post, mas sou iniciante em Java e como você trabalha com isso, você poderia me dar conselhos do que estudar?
Excelente artigo cara, parabéns!!
Stream facilita demais a nossa vida e você soube explicar de forma muito clara. A facilidade que fica para trabalhar com manipulação de dados é incrivel, além de deixar o código bem mais limpo e intuitivo.
Também achei muito bacana o desafio que você propôs no seu Git, para que nós possamos colocar em prática o que você nos ensinou.
Além disso, os comentários aqui do pessoal agregaram ainda mais o seu artigo! Show de bola!!