Обробка Елементів За Допомогою Методу 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. Який результат може вивести цей код?
Дякуємо за ваш відгук!
Запитати АІ
Запитати АІ
Запитайте про що завгодно або спробуйте одне із запропонованих запитань, щоб почати наш чат