Guia Prático de Operações de Stream em Java 8: filter, distinct, skip, map, flatMap, match, find, reduce e peek

I. Operações filter, distinct e skip com Stream

Exemplo de uso básico dessas operações intermediárias.

package exemplos.stream;

import java.util.Arrays;
import java.util.List;
import java.util.stream.Collectors;

public class DemoStreamBase {
    public static void main(String[] args) {
        List<Integer> numeros = Arrays.asList(10, 20, 30, 40, 50, 60, 60, 70, 70, 80);

        // Filtrar apenas múltiplos de 10
        List<Integer> filtrados = numeros.stream()
            .filter(n -> n % 10 == 0)
            .collect(Collectors.toList());
        System.out.println(filtrados); // [10, 20, 30, 40, 50, 60, 60, 70, 70, 80]

        // Remover duplicatas
        List<Integer> unicos = numeros.stream()
            .distinct()
            .collect(Collectors.toList());
        System.out.println(unicos); // [10, 20, 30, 40, 50, 60, 70, 80]

        // Ignorar os primeiros 3 elementos
        List<Integer> ignorados = numeros.stream()
            .skip(3)
            .collect(Collectors.toList());
        System.out.println(ignorados); // [40, 50, 60, 60, 70, 70, 80]

        // Manter apenas os primeiros 4 elementos
        List<Integer> limitados = numeros.stream()
            .limit(4)
            .collect(Collectors.toList());
        System.out.println(limitados); // [10, 20, 30, 40]
    }
}

Saída correspondente:

[10, 20, 30, 40, 50, 60, 60, 70, 70, 80]
[10, 20, 30, 40, 50, 60, 70, 80]
[40, 50, 60, 60, 70, 70, 80]
[10, 20, 30, 40]

II. Opperações map e flatMap com Stream

A operação map transforma cada elemento do stream, enquento flatMap é usada para achatar streams aninhados.

package exemplos.stream;

import java.util.Arrays;
import java.util.List;
import java.util.stream.Collectors;
import java.util.stream.Stream;

public class DemoTransformacaoStream {
    public static void main(String[] args) {
        List<Integer> lista = Arrays.asList(1, 2, 3, 4, 5);

        // Dobrar cada valor
        List<Integer> dobrados = lista.stream()
            .map(valor -> valor * 2)
            .collect(Collectors.toList());
        System.out.println(dobrados); // [2, 4, 6, 8, 10]

        // Extrair nomes de objetos
        List<String> nomes = listarProdutos().stream()
            .map(Produto::getNome)
            .collect(Collectors.toList());
        System.out.println(nomes); // [Laptop, Smartphone, Tablet, Monitor]

        // Exemplo de flatMap: dividir palavras em caracteres únicos
        String[] frases = {"Ola", "Mundo"};
        Stream<String[]> streamArrays = Arrays.stream(frases)
            .map(f -> f.split(""));
        Stream<String> streamCaracteres = streamArrays.flatMap(Arrays::stream);
        List<String> caracteresUnicos = streamCaracteres.distinct().collect(Collectors.toList());
        System.out.println(caracteresUnicos); // [O, l, a, M, u, n, d]
    }

    private static List<Produto> listarProdutos() {
        return Arrays.asList(
            new Produto("Laptop", 1200),
            new Produto("Smartphone", 800),
            new Produto("Tablet", 400),
            new Produto("Monitor", 600)
        );
    }

    // Classe auxiliar para demonstração
    static class Produto {
        private String nome;
        private double preco;

        public Produto(String nome, double preco) {
            this.nome = nome;
            this.preco = preco;
        }

        public String getNome() {
            return nome;
        }
    }
}

Saída:

[2, 4, 6, 8, 10]
[Laptop, Smartphone, Tablet, Monitor]
[O, l, a, M, u, n, d]

flatMap combina um ou mais streams em um único stream. Pode ser entendido como uma operação de mapeamento seguida de achamento.

III. Operações match, find e reduce com Stream

Operações de correspondência, busca e redução.

package exemplos.stream;

import java.util.Arrays;
import java.util.Optional;
import java.util.stream.Stream;

public class DemoOperacoesTerminais {
    public static void main(String[] args) {
        Stream<Integer> fluxo = Arrays.asList(5, 10, 15, 20, 25).stream();

        // Verificações de correspondência
        boolean todosPositivos = fluxo.allMatch(n -> n > 0);
        System.out.println(todosPositivos); // true

        fluxo = Arrays.asList(5, 10, 15, 20, 25).stream();
        boolean algumMaiorQue20 = fluxo.anyMatch(n -> n > 20);
        System.out.println(algumMaiorQue20); // true

        fluxo = Arrays.asList(5, 10, 15, 20, 25).stream();
        boolean nenhumNegativo = fluxo.noneMatch(n -> n < 0);
        System.out.println(nenhumNegativo); // true

        // Encontrar elementos
        fluxo = Arrays.asList(5, 10, 15, 20, 25).stream();
        Optional<Integer> primeiroPar = fluxo.filter(n -> n % 2 == 0).findFirst();
        primeiroPar.ifPresent(System.out::println); // 10

        // Operação de redução: soma
        fluxo = Arrays.asList(5, 10, 15, 20, 25).stream();
        int soma = fluxo.reduce(0, (acumulador, valor) -> acumulador + valor);
        System.out.println(soma); // 75

        // Redução: valor máximo
        fluxo = Arrays.asList(5, 10, 15, 20, 25).stream();
        fluxo.reduce(Integer::max).ifPresent(System.out::println); // 25
    }
}

Saída:

true
true
true
10
75
25

IV. Operação peek com Stream

peek é uma operação intermediária para inspeção durante o processamento do stream, útil para depuração.

package exemplos.stream;

import java.util.List;
import java.util.stream.Collectors;
import java.util.stream.Stream;

public class DemoPeek {
    public static void main(String[] args) {
        // Uso para depuração
        List<String> resultado = Stream.of("Java", "Kotlin", "Python", "Go")
            .filter(lang -> lang.length() > 3)
            .peek(lang -> System.out.println("Filtrado: " + lang))
            .map(String::toUpperCase)
            .peek(lang -> System.out.println("Mapeado: " + lang))
            .collect(Collectors.toList());
        System.out.println(resultado);
        // Saída: Filtrado: Java, Mapeado: JAVA, Filtrado: Kotlin, Mapeado: KOTLIN, Filtrado: Python, Mapeado: PYTHON
        // [JAVA, KOTLIN, PYTHON]

        // Observação: peek não é uma operação terminal; ela requer uma operação terminal como collect para executar.
        // Diferentemente de forEach, que é terminal e retorna void.
    }
}

peek não modifica os elementos, a menos que o objeto seja mutável. Recomenda-se seu uso apenas em ambientes de desenvolvimento e testes para inspecinoar o fluxo de dados.

Tags: Java 8 Stream API Functional Programming Lambda Expressions Stream Operations

Publicado em 6-29 23:12