Notice: This page requires JavaScript to function properly.
Please enable JavaScript in your browser settings or update your browser.
Aprenda Princípios | Fundamentos e Capacidades Funcionais da Stream API
Stream API

bookPrincípios

Os princípios de trabalho com a Stream API são baseados em conceitos fundamentais da programação funcional e avaliação preguiçosa. A seguir, veja os principais princípios da Stream API.

Avaliação Preguiçosa

Um dos principais princípios da Stream API é a avaliação preguiçosa.

Isso significa que operações intermediárias como filter() ou map() não são executadas imediatamente. Elas apenas formam uma cadeia de ações que será executada somente quando uma operação terminal, como collect() ou forEach(), for invocada.

Main.java

Main.java

copy
12345678910111213
package com.example; import java.util.List; public class Main { public static void main(String[] args) { List<String> names = List.of("Alice", "Bob", "Charlie"); long count = names.stream() .filter(name -> name.startsWith("A")) // Forming the operation .count(); // Triggering execution System.out.println(count); // Output: 1 } }

Este código cria um stream a partir de uma lista de strings, filtra os elementos para manter apenas aqueles que começam com a letra A e conta quantos atendem à condição. O resultado (1) é exibido, pois apenas Alice satisfaz a condição.

Estilo de Programação Funcional

A Stream API utiliza expressões lambda e interfaces funcionais para o processamento de dados. Em vez da abordagem imperativa com laços, descreve-se o que deve ser feito com os dados:

Main.java

Main.java

copy
1234567891011121314
package com.example; import java.util.List; public class Main { public static void main(String[] args) { List<String> fruits = List.of("apple", "banana", "cherry", "apricot", "blueberry"); List<String> result = fruits.stream() .map(String::toUpperCase) // Convert all strings to uppercase .skip(3) .toList(); // Collect the result into a new list System.out.println(result); } }

Este código cria um stream a partir de uma lista de frutas, converte as strings para maiúsculas usando map(), e ignora os três primeiros elementos com skip(). O resultado é coletado em uma nova lista e impresso, começando a partir do quarto elemento.

Imutabilidade dos Dados

A Stream API não modifica os dados originais. Todas as operações criam um novo stream ou retornam um resultado sem modificar a coleção ou array. Isso aumenta a segurança dos dados e previne efeitos colaterais inesperados.

Main.java

Main.java

copy
12345678910111213
package com.example; import java.util.List; public class Main { public static void main(String[] args) { List<String> names = List.of("Alice", "Bob", "Charlie"); List<String> updateNames = names.stream() .filter(name -> name.startsWith("A")) .toList(); System.out.println(names); // Output: [Alice, Bob, Charlie] } }

A Stream API filtra a lista de nomes para criar um novo stream, updateNames, que contém apenas elementos que começam com a letra A. No entanto, a lista de nomes original permanece inalterada, pois a Stream API não modifica os dados diretamente, garantindo a segurança dos dados e prevenindo efeitos colaterais.

Streams são consumíveis uma vez

Um stream pode ser utilizado apenas uma vez. Após a realização de uma operação terminal, o stream se torna indisponível para novo processamento.

Main.java

Main.java

copy
123456789101112
package com.example; import java.util.List; import java.util.stream.Stream; public class Main { public static void main(String[] args) { Stream<String> stream = List.of("Alice", "Bob").stream(); stream.forEach(System.out::println); stream.forEach(System.out::println); // Error: Stream has already been used } }

Aqui, é criado um stream a partir de uma lista de strings e cada elemento é impresso no console. Após o stream ser utilizado pela primeira vez, ele não pode ser reutilizado, o que causa um erro caso tente chamar forEach() novamente.

Processamento Paralelo

A Stream API oferece suporte a streams paralelos, permitindo um processamento de dados mais rápido em sistemas multi-core. Este tema é explorado em mais detalhes aqui

Main.java

Main.java

copy
1234567891011
package com.example; import java.util.List; public class Main { public static void main(String[] args) { List<Integer> numbers = List.of(1, 2, 3, 4, 5); numbers.parallelStream() .forEach(System.out::println); // Elements are processed in parallel } }

Este código cria um stream paralelo a partir de uma lista de números e imprime cada elemento no console. O uso de parallelStream() permite o processamento paralelo dos elementos da lista, o que pode acelerar a execução ao lidar com grandes volumes de dados.

Clareza e Legibilidade do Código

O uso da Stream API torna o código mais declarativo. Em vez de descrever como realizar uma tarefa (como ao utilizar laços), descreve-se o que precisa ser feito. Isso melhora a legibilidade e simplifica a manutenção do código.

Exemplo com um laço:

List<String> names = List.of("Alice", "Bob", "Charlie");
for (String name : names) {
    System.out.println(name);
}

Exemplo utilizando Stream API:

List<String> names = List.of("Alice", "Bob", "Charlie");
names.stream().forEach(System.out::println);

No primeiro exemplo de loop, descreve-se explicitamente como iterar pela lista e imprimir os elementos, enquanto no segundo exemplo com o Stream API, apenas se especifica o que precisa ser feito: iterar pelos elementos e imprimi-los. Isso torna o código mais declarativo e legível.

Tudo estava claro?

Como podemos melhorá-lo?

Obrigado pelo seu feedback!

Seção 1. Capítulo 2

Pergunte à IA

expand

Pergunte à IA

ChatGPT

Pergunte o que quiser ou experimente uma das perguntas sugeridas para iniciar nosso bate-papo

Awesome!

Completion rate improved to 2.33

bookPrincípios

Deslize para mostrar o menu

Os princípios de trabalho com a Stream API são baseados em conceitos fundamentais da programação funcional e avaliação preguiçosa. A seguir, veja os principais princípios da Stream API.

Avaliação Preguiçosa

Um dos principais princípios da Stream API é a avaliação preguiçosa.

Isso significa que operações intermediárias como filter() ou map() não são executadas imediatamente. Elas apenas formam uma cadeia de ações que será executada somente quando uma operação terminal, como collect() ou forEach(), for invocada.

Main.java

Main.java

copy
12345678910111213
package com.example; import java.util.List; public class Main { public static void main(String[] args) { List<String> names = List.of("Alice", "Bob", "Charlie"); long count = names.stream() .filter(name -> name.startsWith("A")) // Forming the operation .count(); // Triggering execution System.out.println(count); // Output: 1 } }

Este código cria um stream a partir de uma lista de strings, filtra os elementos para manter apenas aqueles que começam com a letra A e conta quantos atendem à condição. O resultado (1) é exibido, pois apenas Alice satisfaz a condição.

Estilo de Programação Funcional

A Stream API utiliza expressões lambda e interfaces funcionais para o processamento de dados. Em vez da abordagem imperativa com laços, descreve-se o que deve ser feito com os dados:

Main.java

Main.java

copy
1234567891011121314
package com.example; import java.util.List; public class Main { public static void main(String[] args) { List<String> fruits = List.of("apple", "banana", "cherry", "apricot", "blueberry"); List<String> result = fruits.stream() .map(String::toUpperCase) // Convert all strings to uppercase .skip(3) .toList(); // Collect the result into a new list System.out.println(result); } }

Este código cria um stream a partir de uma lista de frutas, converte as strings para maiúsculas usando map(), e ignora os três primeiros elementos com skip(). O resultado é coletado em uma nova lista e impresso, começando a partir do quarto elemento.

Imutabilidade dos Dados

A Stream API não modifica os dados originais. Todas as operações criam um novo stream ou retornam um resultado sem modificar a coleção ou array. Isso aumenta a segurança dos dados e previne efeitos colaterais inesperados.

Main.java

Main.java

copy
12345678910111213
package com.example; import java.util.List; public class Main { public static void main(String[] args) { List<String> names = List.of("Alice", "Bob", "Charlie"); List<String> updateNames = names.stream() .filter(name -> name.startsWith("A")) .toList(); System.out.println(names); // Output: [Alice, Bob, Charlie] } }

A Stream API filtra a lista de nomes para criar um novo stream, updateNames, que contém apenas elementos que começam com a letra A. No entanto, a lista de nomes original permanece inalterada, pois a Stream API não modifica os dados diretamente, garantindo a segurança dos dados e prevenindo efeitos colaterais.

Streams são consumíveis uma vez

Um stream pode ser utilizado apenas uma vez. Após a realização de uma operação terminal, o stream se torna indisponível para novo processamento.

Main.java

Main.java

copy
123456789101112
package com.example; import java.util.List; import java.util.stream.Stream; public class Main { public static void main(String[] args) { Stream<String> stream = List.of("Alice", "Bob").stream(); stream.forEach(System.out::println); stream.forEach(System.out::println); // Error: Stream has already been used } }

Aqui, é criado um stream a partir de uma lista de strings e cada elemento é impresso no console. Após o stream ser utilizado pela primeira vez, ele não pode ser reutilizado, o que causa um erro caso tente chamar forEach() novamente.

Processamento Paralelo

A Stream API oferece suporte a streams paralelos, permitindo um processamento de dados mais rápido em sistemas multi-core. Este tema é explorado em mais detalhes aqui

Main.java

Main.java

copy
1234567891011
package com.example; import java.util.List; public class Main { public static void main(String[] args) { List<Integer> numbers = List.of(1, 2, 3, 4, 5); numbers.parallelStream() .forEach(System.out::println); // Elements are processed in parallel } }

Este código cria um stream paralelo a partir de uma lista de números e imprime cada elemento no console. O uso de parallelStream() permite o processamento paralelo dos elementos da lista, o que pode acelerar a execução ao lidar com grandes volumes de dados.

Clareza e Legibilidade do Código

O uso da Stream API torna o código mais declarativo. Em vez de descrever como realizar uma tarefa (como ao utilizar laços), descreve-se o que precisa ser feito. Isso melhora a legibilidade e simplifica a manutenção do código.

Exemplo com um laço:

List<String> names = List.of("Alice", "Bob", "Charlie");
for (String name : names) {
    System.out.println(name);
}

Exemplo utilizando Stream API:

List<String> names = List.of("Alice", "Bob", "Charlie");
names.stream().forEach(System.out::println);

No primeiro exemplo de loop, descreve-se explicitamente como iterar pela lista e imprimir os elementos, enquanto no segundo exemplo com o Stream API, apenas se especifica o que precisa ser feito: iterar pelos elementos e imprimi-los. Isso torna o código mais declarativo e legível.

Tudo estava claro?

Como podemos melhorá-lo?

Obrigado pelo seu feedback!

Seção 1. Capítulo 2
some-alt