Обробка Елементів за Допомогою Методу forEach()
Ви вже знайомі з методом forEach(), оскільки використовували його на практиці для виведення кожного елемента колекції у консоль. Розгляньмо його детальніше та дослідимо його реалізації.
Він забезпечує зручний спосіб обробки даних у функціональному стилі. Однак важливо зазначити, що порядок обробки залежить від типу потоку та використаного методу.
У Stream API існує дві реалізації методу forEach(). Розглянемо їх по черзі.
Метод forEach
Виконує дію над кожним елементом, але порядок обробки не гарантується у паралельних потоках.
void forEach(Consumer<? super T> action)
Метод forEach() приймає як аргумент Consumer<T> — функціональний інтерфейс, який визначає операцію для кожного елемента у потоці. Зазвичай використовується для логування, виведення інформації або виконання побічних ефектів.
Практичний приклад
В інтернет-магазині користувачі мають отримувати сповіщення про персоналізовані знижки. Оскільки порядок не має значення, використовується forEach() з паралельним потоком для швидшого виконання.
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 } }
Цей код імітує надсилання персоналізованих листів зі знижками для клієнтів. Оскільки використовується parallelStream(), листи надсилаються у довільному порядку для підвищення швидкості. Метод forEach() застосовує вказану дію до кожної електронної адреси.
Метод forEachOrdered
Виконує дію з збереженням порядку елементів, навіть у паралельних потоках.
void forEachOrdered(Consumer<? super T> action)
Як і forEach, цей метод також приймає Consumer<T>, але гарантує, що елементи обробляються у тому ж порядку, в якому вони з'являються в оригінальному потоці. Це корисно, коли важливо зберігати послідовність.
Практичний приклад
Уявіть собі платіжну систему, де кожна транзакція повинна оброблятися в точному порядку надходження. Якщо платежі обробляються не по порядку, це може призвести до помилок, наприклад, неправильного розрахунку балансу.
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); } }
Цей код імітує систему обробки платежів, де транзакції мають оброблятися у порядку їх надходження. Використання parallelStream() підвищує продуктивність, але forEachOrdered() гарантує збереження послідовності.
Порівняння продуктивності: forEach() проти forEachOrdered()
Оцінимо, наскільки швидше виконується forEach() порівняно з forEachOrdered() при роботі з паралельними потоками. Для цього створіть список із 10 мільйонів елементів, обробіть їх обома методами, обчислюючи квадратний корінь кожного числа, та зафіксуйте час виконання.
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); } }
forEach() обробляє елементи без збереження порядку, дозволяючи потоку вільно розподіляти завдання між доступними ядрами процесора. Це максимізує продуктивність, оскільки кожен потік може брати елементи у будь-якому порядку та обробляти їх паралельно без обмежень.
Метод forEachOrdered() зберігає оригінальний порядок елементів, що вимагає додаткової синхронізації. У паралельному потоці елементи спочатку розбиваються на частини для обробки, але їхні результати потім мають бути зібрані у правильному порядку перед передачею у метод обробки.
1. Який функціональний інтерфейс приймає метод forEach?
2. Який результат може вивести цей код?
Дякуємо за ваш відгук!
Запитати АІ
Запитати АІ
Запитайте про що завгодно або спробуйте одне із запропонованих запитань, щоб почати наш чат
Awesome!
Completion rate improved to 2.33
Обробка Елементів за Допомогою Методу forEach()
Свайпніть щоб показати меню
Ви вже знайомі з методом forEach(), оскільки використовували його на практиці для виведення кожного елемента колекції у консоль. Розгляньмо його детальніше та дослідимо його реалізації.
Він забезпечує зручний спосіб обробки даних у функціональному стилі. Однак важливо зазначити, що порядок обробки залежить від типу потоку та використаного методу.
У Stream API існує дві реалізації методу forEach(). Розглянемо їх по черзі.
Метод forEach
Виконує дію над кожним елементом, але порядок обробки не гарантується у паралельних потоках.
void forEach(Consumer<? super T> action)
Метод forEach() приймає як аргумент Consumer<T> — функціональний інтерфейс, який визначає операцію для кожного елемента у потоці. Зазвичай використовується для логування, виведення інформації або виконання побічних ефектів.
Практичний приклад
В інтернет-магазині користувачі мають отримувати сповіщення про персоналізовані знижки. Оскільки порядок не має значення, використовується forEach() з паралельним потоком для швидшого виконання.
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 } }
Цей код імітує надсилання персоналізованих листів зі знижками для клієнтів. Оскільки використовується parallelStream(), листи надсилаються у довільному порядку для підвищення швидкості. Метод forEach() застосовує вказану дію до кожної електронної адреси.
Метод forEachOrdered
Виконує дію з збереженням порядку елементів, навіть у паралельних потоках.
void forEachOrdered(Consumer<? super T> action)
Як і forEach, цей метод також приймає Consumer<T>, але гарантує, що елементи обробляються у тому ж порядку, в якому вони з'являються в оригінальному потоці. Це корисно, коли важливо зберігати послідовність.
Практичний приклад
Уявіть собі платіжну систему, де кожна транзакція повинна оброблятися в точному порядку надходження. Якщо платежі обробляються не по порядку, це може призвести до помилок, наприклад, неправильного розрахунку балансу.
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); } }
Цей код імітує систему обробки платежів, де транзакції мають оброблятися у порядку їх надходження. Використання parallelStream() підвищує продуктивність, але forEachOrdered() гарантує збереження послідовності.
Порівняння продуктивності: forEach() проти forEachOrdered()
Оцінимо, наскільки швидше виконується forEach() порівняно з forEachOrdered() при роботі з паралельними потоками. Для цього створіть список із 10 мільйонів елементів, обробіть їх обома методами, обчислюючи квадратний корінь кожного числа, та зафіксуйте час виконання.
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); } }
forEach() обробляє елементи без збереження порядку, дозволяючи потоку вільно розподіляти завдання між доступними ядрами процесора. Це максимізує продуктивність, оскільки кожен потік може брати елементи у будь-якому порядку та обробляти їх паралельно без обмежень.
Метод forEachOrdered() зберігає оригінальний порядок елементів, що вимагає додаткової синхронізації. У паралельному потоці елементи спочатку розбиваються на частини для обробки, але їхні результати потім мають бути зібрані у правильному порядку перед передачею у метод обробки.
1. Який функціональний інтерфейс приймає метод forEach?
2. Який результат може вивести цей код?
Дякуємо за ваш відгук!