O que são Streams?
Streams em Java são uma abstração para processar coleções de dados de forma declarativa. Elas permitem manipular conjuntos de dados de maneira eficiente e funcional, sem a necessidade de laços explícitos.
Diferença entre Collections e Streams
Collections armazenam elementos e podem ser modificadas.
Streams são fluxos de dados que permitem operações encadeadas, sem modificar a coleção original.
Streams são consumíveis: após processar um fluxo, ele não pode ser reutilizado.
Benefícios das Streams
Código mais conciso: evita laços e estruturas verbosas.
Imutabilidade: não altera os dados originais.
Paralelismo: pode ser processado em paralelo para melhor desempenho.
Operações em Streams
As operações (parte do fluxo de processamento) em Streams podem ser classificadas em três tipos principais:
Operações Iniciais (Criação da Stream): São as operações que criam uma Stream a partir de uma fonte de dados, como listas, arrays ou valores individuais.
Operações Intermediárias: São operações que transformam a Stream em outra Stream sem consumi-la. Essas operações são lazy, ou seja, só são executadas quando uma operação final é chamada.
Operações Finais: São operações que processam os elementos da Stream e encerram seu uso. Após uma operação final, a Stream não pode ser reutilizada.
obs.: As operações iniciais (criação da Stream) são obrigatórias, pois uma Stream precisa ser criada antes de ser usada. As operações intermediárias são opcionais. Você pode criar uma Stream e executar uma operação final diretamente, sem transformação intermediária. As operações finais são obrigatórias se você quiser obter um resultado, pois elas disparam o processamento da Stream.
Exemplos:
Apenas criação e operação final (sem intermediárias)
List<String> nomes = List.of("Ana", "Bruno", "Carlos");
nomes.stream().forEach(System.out::println);Com operação intermediária e final
List<String> nomes = List.of("Ana", "Bruno", "Carlos");
nomes.stream()
.filter(nome -> nome.startsWith("B")) // Operação intermediária
.forEach(System.out::println); // Operação finalApenas criação e operação intermediária (inválido!)
List<String> nomes = List.of("Ana", "Bruno", "Carlos");
Stream<String> filtrada = nomes.stream().filter(nome -> nome.startsWith("B"));
Isso não gera saída nem efeito visível, porque a Stream é lazy (preguiçosa) e só executa as operações intermediárias quando uma operação final for chamada.
Podemos resumir que:
Criação da Stream → Sempre necessária
Operações intermediárias → Opcionais
Operação final → Necessária para disparar o processamento
Criando Streams
A partir de listas:
List<String> nomes = List.of("Ana", "Bruno", "Carlos");
Stream<String> streamNomes = nomes.stream();
streamNomes.forEach(System.out::println);
A partir de arrays:
String[] array = {"A", "B", "C"};
Stream<String> streamArray = Arrays.stream(array);
A partir de valores:
Stream<String> streamValores = Stream.of("Java", "Python", "C++");
Gerando streams infinitas
Stream.iterate(...)
O método iterate gera uma sequência infinita de elementos baseada em uma regra de transformação.
Ele recebe dois argumentos:
1 O valor inicial.
2 Uma função que gera o próximo valor a partir do valor anterior.
Stream<Integer> numeros = Stream.iterate(0, n -> n + 2);
numeros.limit(5).forEach(System.out::println);
Neste caso, iterate começa com 0 e aplica n -> n + 2 para gerar os próximos valores.
Stream.generate(...)
O método generate cria uma Stream infinita a partir de um fornecedor (Supplier), ou seja, um método que gera valores sem depender do estado anterior.
Stream<Double> aleatorios = Stream.generate(Math::random);
aleatorios.limit(5).forEach(System.out::println);
Operações Intermediárias
Operações intermediárias retornam uma nova Stream, permitindo encadeamento.
map(): transforma elementos
List<String> nomes = List.of("ana", "bruno");
nomes.stream()
.map(String::toUpperCase)
.forEach(System.out::println);
filter(): filtra elementos
List<String> nomes = List.of("Ana", "Bruno", "Carlos");
nomes.stream()
.filter(nome -> nome.startsWith("B"))
.forEach(System.out::println);
sorted(): ordena elementos
List<Integer> numeros = List.of(3, 1, 4, 2);
numeros.stream()
.sorted()
.forEach(System.out::println);
distinct(): remove duplicatas
List<Integer> numeros = List.of(1, 2, 2, 3, 3, 4);
numeros.stream()
.distinct()
.forEach(System.out::println);
limit() e skip(): limitar e pular elementos
List<Integer> numeros = List.of(1, 2, 3, 4, 5, 6);
numeros.stream()
.skip(2) // Pula os dois primeiros elementos
.limit(3) // Pega apenas três elementos
.forEach(System.out::println);
Operações Finais
As operações finais processam os dados e encerram a Stream.
forEach(): percorre os elementos
List<String> nomes = List.of("Ana", "Bruno", "Carlos");
nomes.stream().forEach(System.out::println);
count(): conta elementos
long quantidade = List.of("A", "B", "C").stream().count();
System.out.println(quantidade);
collect(): coleta elementos em uma coleção
List<String> listaFiltrada = nomes.stream()
.filter(nome -> nome.startsWith("B"))
.collect(Collectors.toList());
reduce(): reduz elementos a um único valor
List<Integer> numeros = List.of(1, 2, 3, 4, 5);
int soma = numeros.stream()
.reduce(0, Integer::sum);
System.out.println("Soma: " + soma);
Top comments (0)