Notice: This page requires JavaScript to function properly.
Please enable JavaScript in your browser settings or update your browser.
Вивчайте Реальні Приклади Використання Stream API | Секція
Stream API в Java

bookРеальні Приклади Використання Stream API

Свайпніть щоб показати меню

Код — це не лише про функціональність, а й про читабельність. Добре структурований код легше підтримувати, модифікувати та розширювати.

Ви — розробник, якому доручено перевірити чужий код і зробити його кращим. У реальних проєктах код часто пишуть поспіхом, копіюють з різних частин програми або просто не дбають про читабельність. Ваше завдання — не лише зрозуміти код, а й покращити його: зробити чистішим, більш лаконічним і простішим у підтримці.

Зараз ви проводите code review. Ви будете аналізувати реальний фрагмент коду, визначати його слабкі сторони та рефакторити його крок за кроком за допомогою Stream API.

Початок роботи

Уявіть, що у вас є інтернет-магазин, і вам потрібно визначити активних користувачів, які зробили щонайменше три замовлення на суму $10,000 або більше. Це допоможе маркетинговій команді визначити найбільш цінних клієнтів і запропонувати їм персоналізовані пропозиції.

Ось початковий код до рефакторингу:

Main.java

Main.java

copy
12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970
package com.example; import java.util.ArrayList; import java.util.List; public class Main { public static void main(String[] args) { List<User> users = List.of( new User("Alice", true, List.of(new Order(12000), new Order(15000), new Order(11000))), new User("Bob", true, List.of(new Order(8000), new Order(9000), new Order(12000))), new User("Charlie", false, List.of(new Order(15000), new Order(16000), new Order(17000))), new User("David", true, List.of(new Order(5000), new Order(20000), new Order(30000))), new User("Eve", true, List.of(new Order(10000), new Order(10000), new Order(10000), new Order(12000))) ); List<User> premiumUsers = 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) { premiumUsers.add(user); } } } System.out.println("Premium users: " + premiumUsers); } } 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 + "'}"; } }

У вас є два ключові класи:

Order представляє замовлення та містить поле total, яке зберігає суму замовлення. User представляє клієнта магазину з трьома полями:

  • name (ім'я користувача);
  • active (прапорець статусу);
  • orders (список замовлень).

Кожен User містить список об'єктів Order, що моделює реальну ситуацію, коли клієнт робить кілька замовлень.

У main створюється список користувачів, кожен з власним набором замовлень. Програма далі перебирає цей список і перевіряє, чи користувач активний. Якщо ні — його пропускають.

Далі програма перебирає замовлення користувача та рахує, скільки з них на $10,000 або більше. Якщо у користувача є щонайменше три таких замовлення, його додають до списку premiumUsers.

Після обробки всіх користувачів програма виводить преміум-користувачів.

Проблеми з кодом

  • Занадто багато вкладених циклів – ускладнює читання та розуміння;
  • Зайвий код – надто багато ручних перевірок і проміжних змінних;
  • Відсутність декларативного стилю – код виглядає як низькорівнева обробка даних замість високорівневої логіки.

Тепер ви будете рефакторити цей код крок за кроком, використовуючи Stream API, щоб покращити читабельність, зменшити надлишковість і зробити його більш ефективним.

Рефакторинг коду

Перший крок — видалити зовнішній if (user.isActive()) та інтегрувати цю перевірку безпосередньо у Stream API:

List<User> premiumUsers = users.stream()
        .filter(User::isActive)  // Keep only active users
        .toList();

Тепер код став більш декларативним і чітко показує, що відбувається фільтрація активних користувачів. Зайва умова if відсутня — логіка тепер реалізована безпосередньо у Stream API. Проте це лише підготовка даних, тож рухаємося далі!

Далі слід замінити вкладений цикл for (for (Order order : user.getOrders())) на використання stream() всередині filter:

List<User> premiumUsers = users.stream()
        .filter(User::isActive)
        .filter(user -> user.getOrders().stream()
                .filter(order -> order.getTotal() >= 10000)
                .count() >= 3)  // Count orders directly in Stream API
        .toList();

Завдяки усуненню ручного підрахунку код став чистішим та зрозумілішим—тепер за це відповідає count(), що дозволяє працювати зі стрімом без додаткових змінних.

Фінальний рефакторинг коду

Тепер ви маєте повністю відрефакторене рішення, яке вирішує завдання у декларативній та лаконічній формі:

Main.java

Main.java

copy
12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061
package com.example; import java.util.List; public class Main { public static void main(String[] args) { List<User> users = List.of( new User("Alice", true, List.of(new Order(12000), new Order(15000), new Order(11000))), new User("Bob", true, List.of(new Order(8000), new Order(9000), new Order(12000))), new User("Charlie", false, List.of(new Order(15000), new Order(16000), new Order(17000))), new User("David", true, List.of(new Order(5000), new Order(20000), new Order(30000))), new User("Eve", true, List.of(new Order(10000), new Order(10000), new Order(10000), new Order(12000))) ); List<User> premiumUsers = users.stream() .filter(User::isActive) .filter(user -> user.getOrders().stream() .filter(order -> order.getTotal() >= 10000) .count() >= 3) .toList(); System.out.println("Premium users: " + premiumUsers); } } 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 + "'}"; } }

Код тепер коротший та зрозуміліший, оскільки замість ряду ручних перевірок використовується декларативний підхід — акцент на тому, що потрібно зробити, а не на деталізації кожного кроку процесу. Це усуває необхідність у вкладених циклах, роблячи код легшим для читання та підтримки.

Завдяки Stream API відбувається органічне поєднання фільтрації, підрахунку та збору даних в єдиний потік, що робить код більш виразним та ефективним.

Все було зрозуміло?

Як ми можемо покращити це?

Дякуємо за ваш відгук!

Секція 1. Розділ 40

Запитати АІ

expand

Запитати АІ

ChatGPT

Запитайте про що завгодно або спробуйте одне із запропонованих запитань, щоб почати наш чат

Секція 1. Розділ 40
some-alt