Java Streams na Prática: Exemplos e Boas Práticas
- #Java
Este artigo foi adaptado de uma obra originalmente publicada no Medium, escrita por Hùng Trần no Javarevisited, com o título: "Melhores práticas para usar Java Stream", disponível em: https://medium.com/javarevisited/best-practices-for-using-java-stream-f0f7585f13ba. que discute as melhores práticas ao trabalhar com Java Streams. A seguir, abordaremos essas práticas, traduzidas e reorganizadas, para ajudar os desenvolvedores a escreverem códigos mais eficientes e fáceis de manter utilizando Java Streams.
Java Streams são uma ferramenta poderosa que permite manipular coleções de dados de maneira eficiente e elegante. Para acompanhar os exemplos apresentados aqui, recomendamos o uso de uma IDE como IntelliJ IDEA ou Eclipse. Caso prefira, você também pode utilizar um editor de código online como o Repl.it ou JDoodle, onde você pode clicar no botão de "Executar" para testar os exemplos e visualizar os resultados diretamente.
Principais Operações com Java Streams
Abaixo estão algumas das operações mais comuns com Java Streams. Você pode copiar e colar o código no Repl.it ou JDoodle, clicar em Run (Executar), e ver o resultado imediatamente após a execução.
1. filter(): Remove elementos que não atendem a uma condição específica.
import java.util.Arrays;
import java.util.List;
import java.util.stream.Collectors;
public class ExemploFiltro {
public static void main(String[] args) {
List<String> nomes = Arrays.asList("João", "Paulo", "Carlos", "Rita");
List<String> nomesFiltrados = nomes.stream()
.filter(nome -> nome.startsWith("J"))
.collect(Collectors.toList());
System.out.println(nomesFiltrados);
}
}
Resultado:
[João]
2. map(): Converte cada elemento do fluxo para outro tipo.
import java.util.Arrays;
import java.util.List;
import java.util.stream.Collectors;
public class ExemploMap {
public static void main(String[] args) {
List<String> palavras = Arrays.asList("cafe", "codigo", "java");
List<String> palavrasMaiusculas = palavras.stream()
.map(String::toUpperCase)
.collect(Collectors.toList());
System.out.println(palavrasMaiusculas);
}
}
Resultado:
[CAFE, CODIGO, JAVA]
3. flatMap(): Combina múltiplos fluxos em um único fluxo.
import java.util.Arrays;
import java.util.List;
import java.util.stream.Collectors;
public class ExemploFlatMap {
public static void main(String[] args) {
List<List<Integer>> numerosAninhados = Arrays.asList(
Arrays.asList(1, 2),
Arrays.asList(3, 4),
Arrays.asList(5, 6)
);
List<Integer> numerosSimples = numerosAninhados.stream()
.flatMap(List::stream)
.collect(Collectors.toList());
System.out.println(numerosSimples);
}
}
Resultado:
[1, 2, 3, 4, 5, 6]
4. distinct(): Remove elementos duplicados do fluxo.
import java.util.Arrays;
import java.util.List;
import java.util.stream.Collectors;
public class ExemploDistinct {
public static void main(String[] args) {
List<Integer> numeros = Arrays.asList(1, 2, 2, 3, 3, 4, 5, 5);
List<Integer> numerosDistintos = numeros.stream()
.distinct()
.collect(Collectors.toList());
System.out.println(numerosDistintos);
}
}
Resultado:
[1, 2, 3, 4, 5]
5. sorted(): Ordena os elementos do fluxo.
import java.util.Arrays;
import java.util.List;
import java.util.stream.Collectors;
public class ExemploOrdenacao {
public static void main(String[] args) {
List<Integer> numeros = Arrays.asList(3, 1, 4, 1, 5, 9, 2, 6);
List<Integer> numerosOrdenados = numeros.stream()
.sorted()
.collect(Collectors.toList());
System.out.println(numerosOrdenados);
}
}
Resultado:
[1, 1, 2, 3, 4, 5, 6, 9]
6. peek(): Útil para depurar ou inspecionar o fluxo.
import java.util.stream.IntStream;
public class ExemploPeek {
public static void main(String[] args) {
IntStream.range(1, 6)
.peek(n -> System.out.println("Processando: " + n))
.map(n -> n * n)
.forEach(System.out::println);
}
}
Resultado:
Processando: 1
1
Processando: 2
4
Processando: 3
9
Processando: 4
16
Processando: 5
25
7. limit() e skip(): Controlam quantos elementos são processados.
import java.util.stream.IntStream;
public class ExemploLimitSkip {
public static void main(String[] args) {
System.out.println("Limit:");
IntStream.range(1, 100)
.limit(5)
.forEach(System.out::println);
System.out.println("Skip:");
IntStream.range(1, 11)
.skip(5)
.forEach(System.out::println);
}
}
Resultado:
Limit:
1
2
3
4
5
Skip:
6
7
8
9
10
8. reduce(): Executa operações de agregação.
import java.util.Arrays;
public class ExemploReduce {
public static void main(String[] args) {
Integer[] numeros = {1, 2, 3, 4, 5};
int soma = Arrays.stream(numeros)
.reduce(0, Integer::sum);
System.out.println("Soma: " + soma);
}
}
Resultado:
Soma: 15
9. collect(): Converte o fluxo em uma coleção.
import java.util.List;
import java.util.stream.Collectors;
import java.util.stream.Stream;
public class ExemploCollect {
public static void main(String[] args) {
List<Integer> numerosColetados = Stream.of(1, 2, 3, 4, 5)
.collect(Collectors.toList());
System.out.println(numerosColetados);
}
}
Resultado:
[1, 2, 3, 4, 5]
Dicas e Práticas Recomendadas
- Escreva Código Legível
- Coloque cada operação de stream em uma nova linha para facilitar a leitura e depuração do código.
- Cheque para
null
em Operações de Mapeamento - Sempre verifique se o valor não é
null
quando usarmap()
oufilter()
. - Cuidado com o Uso Excessivo de Paralelismo
- Embora
parallelStream()
possa aumentar o desempenho, nem sempre é vantajoso. Utilize-o somente quando for realmente necessário. - Nomeie Variáveis de Forma Clara
- Utilize nomes de variáveis descritivos. Evite nomes como
a
,b
ec
, que dificultam a compreensão do código. - Atenção ao Converter Listas em Mapas
- Ao usar
collect()
para converter listas em mapas, verifique a existência de chaves duplicadas para evitar exceções. - Uso de Optional
Optional
é útil em casos comofindFirst()
oufindAny()
, ajudando a lidar com possíveis valores ausentes.- Evite o Overuse de Streams
- Embora streams facilitem a escrita de código conciso, seu uso excessivo pode tornar o código difícil de manter. Use streams de forma equilibrada.
Java Streams são uma ferramenta valiosa para simplificar a manipulação de coleções de dados. Ao seguir essas práticas recomendadas e usar os exemplos apresentados, você poderá escrever código mais eficiente e de fácil manutenção. Lembre-se de equilibrar o uso de streams com o código imperativo tradicional para evitar complexidades desnecessárias.