List<String> names = List.of(«Alice», «Bob», «Charlie», «David»);
List<String> filtered = names.stream()
.filter(name -> name.length() > 3)
.map(String::toUpperCase)
.toList();
Агрегация данных
List<Integer> numbers = List.of(1, 2, 3, 4, 5);
int sum = numbers.stream()
.reduce(0, Integer::sum);
long count = numbers.stream()
.filter(n -> n % 2 == 0)
.count();
2. Параллельные стримы
Stream API позволяет легко распараллелить обработку данных:
List<Integer> bigData = IntStream.range(0, 1_000_000).boxed().toList();
long evenCount = bigData.parallelStream()
.filter(n -> n % 2 == 0)
.count();
Важно:
3. Ленивые вычисления
Стримы не выполняют операции до вызова терминальной операции (например, collect
, forEach
, reduce
).
List<String> result = names.stream()
.peek(System.out::println) // Не выполнится без терминальной операции
.filter(name -> name.startsWith(«A»))
.toList();
4. Практическое применение
Группировка данных
Map<Integer, List<String>> groupedByNameLength = names.stream()
.collect(Collectors.groupingBy(String::length));
Поиск и проверка условий
boolean hasLongName = names.stream()
.anyMatch(name -> name.length() > 10);
Optional<String> firstLongName = names.stream()
.filter(name -> name.length() > 5)
.findFirst();
5. Продвинутые методы Stream API
5.1. Работа с flatMap
Если элементы стрима сами являются коллекциями, flatMap
помогает «развернуть» их в один общий стрим:
List<List<Integer>> nestedNumbers = List.of(
List.of(1, 2, 3),
List.of(4, 5, 6)
);
List<Integer> flattened = nestedNumbers.stream()
.flatMap(List::stream)
.toList();
// Результат: [1, 2, 3, 4, 5, 6]
Применение:
5.2. Сортировка и ограничение (sorted
, limit
, skip
)
List<Integer> numbers = List.of(5, 3, 8, 1, 2);
// Топ-3 наибольших чисел
List<Integer> top3 = numbers.stream()
.sorted(Comparator.reverseOrder())
.limit(3)
.toList();
// Результат: [8, 5, 3]
// Пропуск первых 2 элементов
List<Integer> skipped = numbers.stream()
.skip(2)
.toList();
// Результат: [8, 1, 2]
5.3. Поиск и сопоставление (anyMatch
, allMatch
, noneMatch
)
List<String> names = List.of(«Alice», «Bob», «Charlie»);
boolean hasA = names.stream().anyMatch(s -> s.contains(«A»)); // true
boolean allLong = names.stream().allMatch(s -> s.length() > 3); // false
boolean noDigits = names.stream().noneMatch(s -> s.matches(«.*\\d.*»)); // true
Где использовать:
6. Работа с примитивными стримами (IntStream
, LongStream
, DoubleStream
)
Для примитивных типов Java предлагает специализированные стримы, которые работают быстрее и избегают автоупаковки.
6.1. Генерация числовых диапазонов
IntStream.range(1, 10) // 1, 2, 3, …, 9
IntStream.rangeClosed(1, 10) // 1, 2, 3, …, 10
6.2. Статистика (sum
, average
, min
, max
)
int sum = IntStream.of(1, 2, 3).sum();
OptionalDouble avg = IntStream.of(1, 2, 3).average();
OptionalInt max = IntStream.of(1, 2, 3).max();
7. Собственные коллекторы (Collectors
)
Класс Collectors
предоставляет множество готовых решений для агрегации данных.
7.1. Группировка (groupingBy
)
Map<Integer, List<String>> namesByLength = names.stream()
.collect(Collectors.groupingBy(String::length));
// {3=[«Bob»], 5=[«Alice»], 7=[«Charlie»]}
7.2. Объединение строк (joining
)
String joined = names.stream()
.collect(Collectors.joining(«, «));
// «Alice, Bob, Charlie»
7.3. Разделение на две группы (partitioningBy
)
Map<Boolean, List<String>> partitioned = names.stream()
.collect(Collectors.partitioningBy(s -> s.length() > 4));
// {false=[«Bob»], true=[«Alice», «Charlie»]}
8. Когда НЕ использовать Stream API?
Несмотря на мощь, стримы подходят не для всех задач:
-
Модификация исходных данных (стримы работают в режиме «только чтение»).
-
Сложные условия обработки, где нужен break
или return
из цикла.
-
Очень маленькие коллекции — накладные расходы могут перевесить преимущества.
Заключение:
Stream API в Java — это мощный инструмент, который позволяет:
✅ Писать чистый и декларативный код, избегая шаблонных циклов
✅ Легко распараллеливать обработку данных без сложных многопоточных конструкций
✅ Оптимизировать производительность за счет ленивых вычислений
✅ Работать с данными любого типа — от коллекций объектов до примитивов
✅ Строить сложные цепочки обработки, сохраняя читаемость кода
Для дальнейшего изучения
-
Углубленное изучение Collectors
-
Кастомные коллекторы через Collector.of()
-
Группировка с дополнительными операциями (mapping
, filtering
)
-
Работа с примитивными стримами
-
IntStream
, LongStream
, DoubleStream
и их особенности
-
Оптимизация производительности для числовых операций
-
Параллельные стримы на практике
-
Интеграция с другими API
-
Оптимизация и отладка стримов
-
Нововведения в современных версиях Java
-
Функциональные интерфейсы и их комбинация со стримами
-
Predicate
, Function
, Consumer
и их кастомные реализации
-
Композиция функций для сложных преобразований