Tags: Java
Você já se viu escrevendo blocos de código com loops for
aninhados para filtrar e transformar uma simples lista em Java? Neste artigo, vamos explorar como a Stream API pode revolucionar seu código, tornando-o mais declarativo, legível e eficiente, transformando tarefas complexas em operações elegantes de uma única linha.
O que é um stream? Stream é uma sequência de elementos que suporta operações de agregação.
O que é operação de agregação? Agrupar informações e transformá-las em uma só.
Stream não armazena dados! Stream, em uma tradução literal como fluxo
, é um processo de dados em sequência, onde cada etapa transforma, reduz ou filtra os elementos de uma fonte (como Arrays, Collections, etc.).
Possui funções intermediárias e terminais, mas o que é isso exatamente?
- As funções intermediárias são aquelas que transformam, filtram ou organizam os dados, sem encerrar o fluxo. Elas retornam um novo stream, permitindo o encadeamento de várias operações.
- As funções terminais são aquelas que finalizam o fluxo e produzem um resultado concreto, encerrando o processo.
Serve para filtrar elementos com base em uma condição. Somente os elementos que satisfazem a expressão booleana passada permanecem no stream.
List<Integer> numeros = Arrays.asList(1, 2, 3, 4, 5, 6);
List<Integer> pares = numeros.stream()
.filter(n -> n % 2 == 0) // Deixa apenas números pares
.toList();
System.out.println(pares); // [2, 4, 6]
É usada para transformar cada elemento do stream em outro tipo de valor ou formato.
List<String> nomes = Arrays.asList("diego", "ana", "paulo");
List<String> nomesMaiusculos = nomes.stream()
.map(String::toUpperCase) // Transforma cada nome em maiúsculo
.toList();
System.out.println(nomesMaiusculos); // [DIEGO, ANA, PAULO]
distinct()
: Remove elementos duplicados.sorted()
: Ordena os elementos (alfabeticamente ou numericamente).limit(n)
: Pega apenas os primeirosn
elementos.skip(n)
: Ignora os primeirosn
elementos.peek()
: Usada para “espiar” o conteúdo durante o processamento, geralmente para depuração.
List<String> lista = Arrays.asList("Diego", "Ana", "Ana", "Paulo", "Lucas", "Luiza");
List<String> resultado = lista.stream()
.filter(nome -> nome.length() > 3) // Deixa apenas nomes com mais de 3 letras
.distinct() // Remove duplicados
.sorted() // Ordena alfabeticamente
.peek(System.out::println) // Mostra cada nome durante o fluxo
.limit(4) // Limita aos 4 primeiros resultados
.toList();
System.out.println("Resultado final: " + resultado);
Coleta os elementos do stream e os transforma em uma coleção (como List
, Set
) ou outro tipo de resultado.
List<Integer> numeros = Arrays.asList(1, 2, 3, 4, 5, 6);
List<Integer> pares = numeros.stream()
.filter(n -> n % 2 == 0) // mantém apenas pares
.collect(Collectors.toList()); // coleta em uma lista
System.out.println(pares); // [2, 4, 6]
Executa uma ação para cada elemento do stream. Muito útil para imprimir ou aplicar alguma operação.
List<String> nomes = Arrays.asList("Diego", "Ana", "Paulo");
nomes.stream()
.forEach(System.out::println);
// Saída:
// Diego
// Ana
// Paulo
Retorna a quantidade de elementos que passaram pelo stream, útil para estatísticas.
long quantidadePares = numeros.stream()
.filter(n -> n % 2 == 0)
.count();
System.out.println("Quantidade de pares: " + quantidadePares); // 3
Reduz todos os elementos do stream a um único valor, aplicando uma operação cumulativa, como soma, multiplicação ou concatenação.
int soma = numeros.stream()
.reduce(0, (a, b) -> a + b); // soma todos os números
System.out.println("Soma total: " + soma); // 21
Verificam se algum, todos ou nenhum elemento atende a uma condição.
boolean temPar = numeros.stream().anyMatch(n -> n % 2 == 0);
System.out.println("Tem algum número par? " + temPar); // true
Retornam o primeiro ou qualquer elemento do stream que corresponda a um critério. Retornam um Optional
.
Optional<Integer> primeiroPar = numeros.stream()
.filter(n -> n % 2 == 0)
.findFirst();
System.out.println("Primeiro par: " + primeiroPar.orElse(-1)); // 2
-
Evitar reusar a mesma stream: Streams em Java são consumidos por operações terminais. Tentar reutilizar o mesmo stream gera
IllegalStateException
, pois o fluxo de dados já foi “fechado”.Exception in thread "main" java.lang.IllegalStateException: stream has already been operated upon or closed
- Preferir operações intermediárias
stateless
quando possível: Operaçõesstateless
não mantêm estado entre os elementos do fluxo.
List<Integer> numeros = Arrays.asList(1, 2, 3, 4, 5, 6); numeros.stream() .filter(n -> n % 2 == 0) // stateless .map(n -> n * 2) // stateless .forEach(System.out::println);
Em streams grandes, operações
stateful
podem ser lentas ou causar uso excessivo de memória. Operaçõesstateless
permitem paralelização mais eficiente. - Preferir operações intermediárias
Como vimos, a Stream API do Java é mais do que uma ferramenta: é uma mudança de paradigma que promove um código mais limpo e funcional. Ao dominar operações como map
e filter
, você escreve menos e expressa mais.
Agora é sua vez! Adicione seus conhecimentos ao repositório do projeto: github.com/diegooilv/stream-api-java
! Veja a pasta exemplos/
e adicione um exemplo seu!
- Documentação Oficial do Java Stream: https://docs.oracle.com/javase/8/docs/api/java/util/stream/Stream.html
- Tutorial (Baeldung): https://www.baeldung.com/java-8-streams
- Repositório com o Código: github.com/diegooilv/stream-api-java