Stream API e Manipulação de Listas e Arrays
Introdução
A Stream API introduzida no Java 8 revolucionou a forma como manipulamos coleções de dados. Ela permite processar conjuntos de dados de maneira declarativa, expressando o "o quê" ao invés do "como". Isso resulta em um código mais conciso, legível e fácil de manter.
Neste guia, exploraremos como a Stream API funciona, como manipulá-la com listas e arrays, e forneceremos exemplos práticos para ilustrar seu uso.
O que é a Stream API?
A Stream API é um conjunto de classes e interfaces que permite processar dados de coleções (como List, Set, Map) e arrays de forma funcional e paralela. Uma Stream representa uma sequência de elementos suportando operações sequenciais e paralelas.
Características Principais
Declaratividade: Permite descrever o que fazer, não como fazer.
Pipeline de Operações: As Streams permitem encadear operações intermediárias e terminais.
Lazy Evaluation: Operações intermediárias são avaliadas apenas quando necessário.
Possibilidade de Paralelismo: Fácil conversão para processamento paralelo.
Como Funcionam as Streams?
As Streams funcionam em três etapas principais:
Fonte (Source): Origem dos dados, como coleções ou arrays.
Operações Intermediárias: Transformam a Stream em outra Stream (e.g.,
filter
,map
).Operação Terminal: Finaliza o processamento e retorna um resultado (e.g.,
collect
,forEach
).
Fluxo Básico
Manipulação de Listas com Stream API
Exemplo 1: Filtrar e Mapear uma Lista de Números
Operações Comuns
filter(Predicate T predicate): Seleciona elementos que correspondem a um predicado.
map(Function<T, R> mapper): Transforma cada elemento em outro.
collect(Collector<T, A, R> collector): Reúne os elementos finais em uma coleção ou outro tipo de resultado.
Manipulação de Arrays com Stream API
Exemplo 2: Processar um Array de Strings
Criando Streams a partir de Arrays
Arrays.stream(array): Cria uma Stream a partir de um array.
Operações Intermediárias em Detalhe
filter()
Filtra elementos com base em um predicado.
map()
Transforma elementos em outros tipos ou formatos.
flatMap()
Desenvolve uma função que retorna uma Stream para cada elemento e as concatena.
sorted()
Ordena os elementos.
Operações Terminais em Detalhe
collect()
Coleta os elementos em uma coleção ou outro tipo de resultado.
forEach()
Executa uma ação para cada elemento.
reduce()
Combina os elementos em um único resultado.
anyMatch(), allMatch(), noneMatch()
Testam se algum, todos ou nenhum elemento correspondem a um predicado.
Processamento Paralelo com Streams
É possível paralelizar o processamento simplesmente chamando parallelStream()
ao invés de stream()
.
Exemplo
Nota: O processamento paralelo pode melhorar a performance em conjuntos de dados grandes, mas deve ser usado com cuidado devido a questões de thread safety.
Exemplo Prático Completo
Cenário: Processar uma Lista de Produtos
Vamos supor que temos uma lista de produtos e queremos aplicar descontos, filtrar produtos em promoção e coletar os nomes em uma lista.
Convertendo Arrays em Listas e vice-versa
De Array para Lista
De Lista para Array
Usando Streams
Boas Práticas
Evitar Estados Mutáveis: Evite modificar o estado de objetos dentro das operações de Stream.
Operações de Curto-Circuito: Use
findFirst
,findAny
,anyMatch
, etc., para eficiência.Limpar e Conciso: Mantenha as expressões lambda simples e legíveis.
Evitar Processamento Paralelo Desnecessário: Use
parallelStream
apenas quando apropriado.
Comparação com Abordagem Imperativa
Abordagem Imperativa
Abordagem Declarativa com Streams
Vantagens da Abordagem Declarativa:
Código mais conciso e legível.
Facilita o paralelismo.
Expressa a intenção sem detalhes de implementação.
Exercícios Práticos
Filtrar Números Ímpares e Multiplicar por 3:
Dada uma lista de números inteiros, filtre os números ímpares, multiplique por 3 e colecione em uma lista.
Contar Ocorrências de Palavras:
Dada uma lista de palavras, conte quantas vezes cada palavra aparece usando Streams.
Agrupar Pessoas por Idade:
Dada uma lista de objetos
Pessoa
com nome e idade, agrupe as pessoas por idade usandoCollectors.groupingBy
.
Conclusão
A Stream API é uma ferramenta poderosa que permite processar coleções de dados de forma eficiente e expressiva. Compreender como utilizá-la para manipular listas e arrays é essencial para escrever código Java moderno e de alta qualidade.
Referências
Java Documentation: Stream API
Oracle Tutorial: Processing Data with Java SE 8 Streams
Livro: Java 8 in Action - Raoul-Gabriel Urma, Mario Fusco, Alan Mycroft
Observações Finais
Pratique escrevendo código com Streams para se familiarizar com as operações.
Lembre-se de que nem todas as operações precisam ser feitas com Streams; escolha a abordagem que torna o código mais claro.
Esteja atento ao desempenho e à legibilidade do código ao usar Streams.