Продуктивність у Stream API
Свайпніть щоб показати меню
Ви успішно рефакторили код, зробивши його чистішим, коротшим та більш виразним.
Але як щодо продуктивності? Наскільки ефективний Stream API у порівнянні з традиційними циклами? Чи можна прискорити його за допомогою parallelStream()? Дізнаємося!
Вимірювання часу виконання
Для об'єктивного порівняння продуктивності буде створено тестовий набір даних із 100,000 users та різними orders. Далі буде реалізовано три підходи до фільтрації:
- Традиційний
for-loop– класичний підхід із вкладеними циклами; - Stream API (
stream()) – сучасний декларативний метод; - Parallel Stream API (
parallelStream()) – багатопотокова обробка.
Вимірювання часу виконання здійснюється за допомогою System.nanoTime(), який забезпечує високоточні різниці часу.
Реалізація тесту
Буде згенеровано 100,000 users, кожен із яких матиме orders понад $10,000, і виконано всі три підходи, щоб визначити, який із них покаже найкращий результат.
Main.java
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100package com.example; import java.util.ArrayList; import java.util.List; public class Main { public static void main(String[] args) { List<User> users = generateUsers(100000); // Measuring traditional loop execution time long startTime = System.nanoTime(); List<User> premiumUsersLoop = new ArrayList<>(); for (User user : users) { if (user.isActive()) { int totalOrders = 0; for (Order order : user.getOrders()) { if (order.getTotal() >= 10000) { totalOrders++; } } if (totalOrders >= 3) { premiumUsersLoop.add(user); } } } long endTime = System.nanoTime(); System.out.println("For-loop: " + (endTime - startTime) / 1_000_000 + " ms"); // Measuring Stream API execution time startTime = System.nanoTime(); List<User> premiumUsersStream = users.stream() .filter(User::isActive) .filter(user -> user.getOrders().stream() .filter(order -> order.getTotal() >= 10000) .count() >= 3) .toList(); endTime = System.nanoTime(); System.out.println("Stream API: " + (endTime - startTime) / 1_000_000 + " ms"); // Measuring Parallel Stream API execution time startTime = System.nanoTime(); List<User> premiumUsersParallelStream = users.parallelStream() .filter(User::isActive) .filter(user -> user.getOrders().stream() .filter(order -> order.getTotal() >= 10000) .count() >= 3) .toList(); endTime = System.nanoTime(); System.out.println("Parallel Stream API: " + (endTime - startTime) / 1_000_000 + " ms"); } private static List<User> generateUsers(int count) { List<User> users = new ArrayList<>(); for (int i = 0; i < count; i++) { users.add(new User("User" + i, true, List.of( new Order(14000), new Order(5000), new Order(7000) ))); } return users; } } class Order { private final double total; public Order(double total) { this.total = total; } public double getTotal() { return total; } } class User { private final String name; private final boolean active; private final List<Order> orders; public User(String name, boolean active, List<Order> orders) { this.name = name; this.active = active; this.orders = orders; } public boolean isActive() { return active; } public List<Order> getOrders() { return orders; } @Override public String toString() { return "User{name='" + name + "'}"; } }
Після запуску тесту з 100,000 users, можна спостерігати такі тенденції:
Традиційний for-loop є швидшим для простих операцій, оскільки уникає накладних витрат на створення потоку та додаткових об'єктів. Він показує найкращі результати, коли пріоритетом є максимальна швидкість.
Stream API іноді трохи повільніший через додаткові об'єкти потоку, але значно підвищує читабельність та зручність підтримки коду.
Parallel Stream API може прискорити обробку на багатоядерних системах, але не завжди. Якщо набір даних невеликий, накладні витрати на керування потоками можуть навіть уповільнити виконання. Найкраще підходить для складних обчислень, але не для зміни спільних змінних, оскільки потоки виконуються незалежно.
Підсумок
Stream API — це потужний інструмент, який робить код більш читабельним та лаконічним. Однак щодо продуктивності важливо розуміти його обмеження. У деяких випадках традиційні for-цикли працюють швидше, особливо при роботі з малими наборами даних. parallelStream() може підвищити швидкість обробки, але потребує тестування, щоб переконатися у реальній користі.
Отже, вибір підходу має бути усвідомленим: якщо важлива читабельність, використовуйте Stream API; якщо критична продуктивність — тестуйте та вимірюйте!
1. Який підхід зазвичай є найшвидшим для простих операцій?
2. Чому Stream API може бути повільнішим за звичайний цикл?
3. Коли використання parallelStream() може уповільнити виконання?
Дякуємо за ваш відгук!
Запитати АІ
Запитати АІ
Запитайте про що завгодно або спробуйте одне із запропонованих запитань, щоб почати наш чат