Угруповання. Паралельні потоки
Угруповання даних у Stream API
Угруповання — це потужна функція в Stream API, яка дозволяє розподіляти елементи потоку на групи на основі певного критерію. Основний метод для виконання угруповання — `Collectors.groupingBy()`. Цей метод зберігає результат у вигляді `Map`, де ключем є групувальний критерій, а значенням — колекція елементів, які відповідають цьому ключу.
Приклад угруповання
Синтаксис `Collectors.groupingBy`:
Map<K, List<T>> groupingBy(Function<? super T, ? extends K> classifier);
Угруповання за довжиною рядків:
import java.util.*;
import java.util.stream.Collectors;
public class Main {
public static void main(String[] args) {
List<String> names = Arrays.asList(“Ольга”, “Іван”, “Марія”, “Олег”);
// Угруповання рядків за їхньою довжиною
Map<Integer, List<String>> groupedByLength = names.stream()
.collect(Collectors.groupingBy(String::length));
System.out.println(groupedByLength);
}
}
Виведення:
{4=[Іван, Олег], 5=[Ольга, Марія]}
Розширене угруповання
Можна використовувати додаткові колектори для додаткової обробки елементів у групах.
Приклад угруповання з підрахунком кількості елементів у кожній групі:
import java.util.*;
import java.util.stream.Collectors;
public class Main {
public static void main(String[] args) {
List<String> names = Arrays.asList(“Ольга”, “Іван”, “Марія”, “Олег”);
// Угруповання з підрахунком кількості елементів у кожній групі
Map<Integer, Long> groupedByLength = names.stream()
.collect(Collectors.groupingBy(String::length, Collectors.counting()));
System.out.println(groupedByLength);
}
}
Виведення:
{4=2, 5=2}
У цьому прикладі ми використовуємо `Collectors.counting()` для підрахунку кількості елементів у кожній групі.
Паралельні потоки (Parallel Streams)
Паралельні потоки дозволяють виконувати обробку даних у декілька потоків (thread), щоб прискорити виконання програм на багатоядерних процесорах. Використання паралельних потоків розподіляє роботу на декілька потоків для одночасної обробки елементів потоку.
Створення паралельного потоку
Щоб створити паралельний потік, можна використовувати метод `parallelStream()` або викликати метод `parallel()` на звичайному потоці.
List<String> names = Arrays.asList(“Ольга”, “Іван”, “Марія”, “Олег”);
// Паралельний потік
names.parallelStream()
.forEach(name -> System.out.println(name + ” ” + Thread.currentThread().getName()));
Виведення (порядок може бути непередбачуваним через паралельне виконання):
Іван ForkJoinPool.commonPool-worker-3
Ольга ForkJoinPool.commonPool-worker-1
Марія ForkJoinPool.commonPool-worker-4
Олег ForkJoinPool.commonPool-worker-2
Основні моменти паралельних потоків
- Паралельність: Паралельні потоки розподіляють обробку даних між кількома потоками. Це може прискорити виконання, якщо програма працює на багатоядерному процесорі.
- Непередбачуваний порядок виконання: Оскільки елементи обробляються в різних потоках, порядок обробки елементів може бути не таким, як у послідовних потоках.
- Безпека: При використанні паралельних потоків потрібно бути обережним із доступом до загальних ресурсів, щоб уникнути проблем конкурентного доступу (наприклад, використання синхронізації або об’єктів блокування).
Приклад використання паралельного потоку для підрахунку суми:
import java.util.Arrays;
import java.util.List;
public class Main {
public static void main(String[] args) {
List<Integer> numbers = Arrays.asList(1, 2, 3, 4, 5, 6);
// Підрахунок суми з використанням паралельного потоку
int sum = numbers.parallelStream()
.reduce(0, Integer::sum);
System.out.println(“Сума: ” + sum);
}
}
Потенційні проблеми з паралельними потоками
- Неправильні результати: Якщо методи не є асоціативними або мають побічні ефекти, результати паралельної обробки можуть бути некоректними.
- Низька продуктивність на маленьких наборах даних: Паралельна обробка має накладні витрати на створення потоків і координацію роботи між ними, тому для невеликих наборів даних вона може бути менш ефективною, ніж послідовна обробка.
- Синхронізація: Якщо потоки працюють із загальними ресурсами, потрібна синхронізація для уникнення конкурентного доступу до даних.
Висновок
- Угруповання в Stream API дозволяє легко розподіляти елементи потоку на групи за допомогою методу `Collectors.groupingBy()`.
- Паралельні потоки надають можливість одночасної обробки даних, що може значно прискорити виконання програм на багатоядерних процесорах.
- Паралельні потоки є потужним інструментом, але їхнє використання вимагає обережного підходу через можливі проблеми з конкурентністю та продуктивністю на малих наборах даних.