DEV Community

FUNDAMENTOS JAVA
FUNDAMENTOS JAVA

Posted on

2

Streams em Java

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:

  1. Apenas criação e operação final (sem intermediárias)
    List<String> nomes = List.of("Ana", "Bruno", "Carlos");
    nomes.stream().forEach(System.out::println);

  2. 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 final

  3. Apenas 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);

AWS Security LIVE!

Join us for AWS Security LIVE!

Discover the future of cloud security. Tune in live for trends, tips, and solutions from AWS and AWS Partners.

Learn More

Top comments (0)

Billboard image

The Next Generation Developer Platform

Coherence is the first Platform-as-a-Service you can control. Unlike "black-box" platforms that are opinionated about the infra you can deploy, Coherence is powered by CNC, the open-source IaC framework, which offers limitless customization.

Learn more