Processamento de Elementos com o Método forEach()
Você já está familiarizado com o método forEach(), pois já o utilizou na prática para imprimir cada elemento de uma coleção no console. Vamos analisá-lo mais de perto e explorar suas implementações.
Oferece uma maneira conveniente de processar dados em estilo funcional. No entanto, é importante observar que a ordem de processamento depende do tipo de stream e do método utilizado.
Existem duas implementações do método forEach() na Stream API. Vamos analisá-las uma a uma.
Método forEach
Executa uma ação em cada elemento, mas a ordem de processamento não é garantida em streams paralelos.
void forEach(Consumer<? super T> action)
O método forEach() recebe um Consumer<T> como argumento—uma interface funcional que define uma operação para cada elemento no stream. É comumente utilizado para registro de logs, impressão ou execução de efeitos colaterais.
Exemplo prático
Em uma loja online, usuários precisam receber notificações sobre descontos personalizados. Como a ordem não é relevante, forEach() é utilizado com um stream paralelo para execução mais rápida.
Main.java
1234567891011121314151617181920212223package com.example; import java.util.List; import java.util.stream.Stream; public class Main { public static void main(String[] args) { List<String> customers = List.of( "alice@example.com", "bob@example.com", "charlie@example.com" ); // Parallel stream for faster processing Stream<String> customerStream = customers.parallelStream(); customerStream.forEach(email -> sendDiscountEmail(email)); } private static void sendDiscountEmail(String email) { System.out.println("Sending discount email to: " + email); // Simulating email sending process } }
Este código simula o envio de e-mails de desconto personalizados para clientes. Como parallelStream() é utilizado, os e-mails são enviados em ordem aleatória para maior velocidade. O método forEach() aplica a ação fornecida a cada e-mail.
Método forEachOrdered
Executa uma ação preservando a ordem dos elementos, mesmo em streams paralelos.
void forEachOrdered(Consumer<? super T> action)
Assim como forEach, este método também aceita um Consumer<T>, mas garante que os elementos sejam processados na mesma ordem em que aparecem no stream original. Isso é útil quando a manutenção da sequência é fundamental.
Exemplo prático
Imagine um sistema de pagamentos onde cada transação deve ser processada exatamente na ordem em que chega. Se os pagamentos forem tratados fora de ordem, podem ocorrer erros, como cálculos incorretos de saldo.
Main.java
123456789101112131415161718192021package com.example; import java.util.List; import java.util.stream.Stream; public class Main { public static void main(String[] args) { List<String> payments = List.of( "Payment #5001 - $100", "Payment #5002 - $250", "Payment #5003 - $75" ); Stream<String> paymentStream = payments.parallelStream(); paymentStream.forEachOrdered(payment -> processPayment(payment)); } private static void processPayment(String payment) { System.out.println("Processing payment: " + payment); } }
Este código simula um sistema de processamento de pagamentos onde as transações devem ser tratadas na ordem em que chegam. O uso de parallelStream() aumenta a performance, mas forEachOrdered() garante que a sequência seja mantida.
Comparação de Performance: forEach() vs. forEachOrdered()
Vamos medir o quanto o forEach() é mais rápido em relação ao forEachOrdered() ao trabalhar com streams paralelos. Para isso, será criada uma lista com 10 milhões de elementos, processando-os com ambos os métodos ao calcular a raiz quadrada de cada número, e registrando o tempo de execução.
Main.java
12345678910111213141516171819202122232425262728293031package com.example; import java.util.List; import java.util.stream.IntStream; import java.util.ArrayList; public class Main { public static void main(String[] args) { List<Integer> numbers = new ArrayList<>(); IntStream.range(0, 10_000_000).forEach(numbers::add); // Create a list with 10 million elements // Measure execution time of `forEach()` long startTime = System.nanoTime(); numbers.parallelStream().forEach(num -> process(num)); long forEachTime = System.nanoTime() - startTime; // Measure execution time of `forEachOrdered()` startTime = System.nanoTime(); numbers.parallelStream().forEachOrdered(num -> process(num)); long forEachOrderedTime = System.nanoTime() - startTime; // Print results System.out.println("forEach execution time: " + forEachTime / 1_000_000 + " ms"); System.out.println("forEachOrdered execution time: " + forEachOrderedTime / 1_000_000 + " ms"); } private static void process(int num) { // Simulate workload Math.sqrt(num); } }
O método forEach() processa elementos sem preservar a ordem, permitindo que o stream distribua livremente as tarefas entre os núcleos do processador disponíveis. Isso maximiza o desempenho, pois cada thread pode selecionar elementos em qualquer ordem e processá-los em paralelo sem restrições.
O método forEachOrdered() preserva a ordem original dos elementos, o que exige sincronização adicional. Em um stream paralelo, os elementos são inicialmente divididos em partes para processamento, mas seus resultados precisam ser remontados na ordem correta antes de serem passados para o método de processamento.
1. Qual interface funcional o método forEach aceita?
2. Qual saída esse código pode produzir?
Obrigado pelo seu feedback!
Pergunte à IA
Pergunte à IA
Pergunte o que quiser ou experimente uma das perguntas sugeridas para iniciar nosso bate-papo
Awesome!
Completion rate improved to 2.33
Processamento de Elementos com o Método forEach()
Deslize para mostrar o menu
Você já está familiarizado com o método forEach(), pois já o utilizou na prática para imprimir cada elemento de uma coleção no console. Vamos analisá-lo mais de perto e explorar suas implementações.
Oferece uma maneira conveniente de processar dados em estilo funcional. No entanto, é importante observar que a ordem de processamento depende do tipo de stream e do método utilizado.
Existem duas implementações do método forEach() na Stream API. Vamos analisá-las uma a uma.
Método forEach
Executa uma ação em cada elemento, mas a ordem de processamento não é garantida em streams paralelos.
void forEach(Consumer<? super T> action)
O método forEach() recebe um Consumer<T> como argumento—uma interface funcional que define uma operação para cada elemento no stream. É comumente utilizado para registro de logs, impressão ou execução de efeitos colaterais.
Exemplo prático
Em uma loja online, usuários precisam receber notificações sobre descontos personalizados. Como a ordem não é relevante, forEach() é utilizado com um stream paralelo para execução mais rápida.
Main.java
1234567891011121314151617181920212223package com.example; import java.util.List; import java.util.stream.Stream; public class Main { public static void main(String[] args) { List<String> customers = List.of( "alice@example.com", "bob@example.com", "charlie@example.com" ); // Parallel stream for faster processing Stream<String> customerStream = customers.parallelStream(); customerStream.forEach(email -> sendDiscountEmail(email)); } private static void sendDiscountEmail(String email) { System.out.println("Sending discount email to: " + email); // Simulating email sending process } }
Este código simula o envio de e-mails de desconto personalizados para clientes. Como parallelStream() é utilizado, os e-mails são enviados em ordem aleatória para maior velocidade. O método forEach() aplica a ação fornecida a cada e-mail.
Método forEachOrdered
Executa uma ação preservando a ordem dos elementos, mesmo em streams paralelos.
void forEachOrdered(Consumer<? super T> action)
Assim como forEach, este método também aceita um Consumer<T>, mas garante que os elementos sejam processados na mesma ordem em que aparecem no stream original. Isso é útil quando a manutenção da sequência é fundamental.
Exemplo prático
Imagine um sistema de pagamentos onde cada transação deve ser processada exatamente na ordem em que chega. Se os pagamentos forem tratados fora de ordem, podem ocorrer erros, como cálculos incorretos de saldo.
Main.java
123456789101112131415161718192021package com.example; import java.util.List; import java.util.stream.Stream; public class Main { public static void main(String[] args) { List<String> payments = List.of( "Payment #5001 - $100", "Payment #5002 - $250", "Payment #5003 - $75" ); Stream<String> paymentStream = payments.parallelStream(); paymentStream.forEachOrdered(payment -> processPayment(payment)); } private static void processPayment(String payment) { System.out.println("Processing payment: " + payment); } }
Este código simula um sistema de processamento de pagamentos onde as transações devem ser tratadas na ordem em que chegam. O uso de parallelStream() aumenta a performance, mas forEachOrdered() garante que a sequência seja mantida.
Comparação de Performance: forEach() vs. forEachOrdered()
Vamos medir o quanto o forEach() é mais rápido em relação ao forEachOrdered() ao trabalhar com streams paralelos. Para isso, será criada uma lista com 10 milhões de elementos, processando-os com ambos os métodos ao calcular a raiz quadrada de cada número, e registrando o tempo de execução.
Main.java
12345678910111213141516171819202122232425262728293031package com.example; import java.util.List; import java.util.stream.IntStream; import java.util.ArrayList; public class Main { public static void main(String[] args) { List<Integer> numbers = new ArrayList<>(); IntStream.range(0, 10_000_000).forEach(numbers::add); // Create a list with 10 million elements // Measure execution time of `forEach()` long startTime = System.nanoTime(); numbers.parallelStream().forEach(num -> process(num)); long forEachTime = System.nanoTime() - startTime; // Measure execution time of `forEachOrdered()` startTime = System.nanoTime(); numbers.parallelStream().forEachOrdered(num -> process(num)); long forEachOrderedTime = System.nanoTime() - startTime; // Print results System.out.println("forEach execution time: " + forEachTime / 1_000_000 + " ms"); System.out.println("forEachOrdered execution time: " + forEachOrderedTime / 1_000_000 + " ms"); } private static void process(int num) { // Simulate workload Math.sqrt(num); } }
O método forEach() processa elementos sem preservar a ordem, permitindo que o stream distribua livremente as tarefas entre os núcleos do processador disponíveis. Isso maximiza o desempenho, pois cada thread pode selecionar elementos em qualquer ordem e processá-los em paralelo sem restrições.
O método forEachOrdered() preserva a ordem original dos elementos, o que exige sincronização adicional. Em um stream paralelo, os elementos são inicialmente divididos em partes para processamento, mas seus resultados precisam ser remontados na ordem correta antes de serem passados para o método de processamento.
1. Qual interface funcional o método forEach aceita?
2. Qual saída esse código pode produzir?
Obrigado pelo seu feedback!