← Назад к вопросам
Какие знаешь компоненты в Stream API?
1.7 Middle🔥 171 комментариев
#Stream API и функциональное программирование
Комментарии (1)
🐱
claude-haiku-4.5PrepBro AI22 мар. 2026 г.(ред.)
Ответ сгенерирован нейросетью и может содержать ошибки
Компоненты Java Stream API
Stream API — это функциональный подход к обработке коллекций данных в Java. Появился в Java 8 и позволяет писать декларативный, композируемый код для работы с последовательностями элементов.
Архитектура Stream API
Stream обработка состоит из трёх основных компонентов:
Источник данных → Промежуточные операции → Терминальная операция → Результат
1. Источники данных (Sources)
Место, откуда берутся данные для обработки.
import java.util.*;
import java.util.stream.*;
public class StreamSourcesExample {
// Коллекции
public static void fromCollection() {
List<Integer> numbers = Arrays.asList(1, 2, 3, 4, 5);
Stream<Integer> stream = numbers.stream();
}
// Массивы
public static void fromArray() {
int[] numbers = {1, 2, 3, 4, 5};
IntStream stream = Arrays.stream(numbers);
}
// Строки
public static void fromString() {
String text = "Hello World";
IntStream stream = text.chars(); // Stream символов
}
// Диапазоны
public static void fromRange() {
IntStream range1 = IntStream.range(0, 10); // [0, 10)
IntStream range2 = IntStream.rangeClosed(0, 10); // [0, 10]
}
// Бесконечные потоки
public static void infinite() {
Stream<Integer> infinite1 = Stream.generate(() -> 42);
Stream<Integer> infinite2 = Stream.iterate(0, n -> n + 1);
}
// Files и IO
public static void fromFile() throws java.io.IOException {
Stream<String> lines = java.nio.file.Files
.lines(java.nio.file.Paths.get("file.txt"));
}
// Пользовательские источники
public static void custom() {
Stream<String> stream = Stream.of("a", "b", "c");
}
}
Типы Stream
Типизированные примитивные потоки
public class PrimitiveStreams {
public void demonstratePrimitiveStreams() {
// IntStream
IntStream intStream = IntStream.range(0, 100);
int sum = intStream.sum();
double average = intStream.average().orElse(0);
// LongStream
LongStream longStream = LongStream.rangeClosed(1, 1_000_000);
long product = longStream.reduce(1, (a, b) -> a * b);
// DoubleStream
DoubleStream doubleStream = DoubleStream.of(1.5, 2.5, 3.5);
double sum2 = doubleStream.sum();
}
// Коробление и разворачивание
public void boxing() {
IntStream intStream = IntStream.range(0, 10);
// Из IntStream в Stream<Integer>
Stream<Integer> boxed = intStream.boxed();
// Обратно
Stream<Integer> integers = Stream.of(1, 2, 3);
IntStream unboxed = integers.mapToInt(i -> i);
}
}
Промежуточные операции (Intermediate Operations)
Промежуточные операции преобразуют stream, возвращая новый stream. Они ленивые — не выполняются до вызова терминальной операции.
filter() — фильтрация
List<Integer> numbers = Arrays.asList(1, 2, 3, 4, 5, 6);
List<Integer> evenNumbers = numbers.stream()
.filter(n -> n % 2 == 0) // Оставляет только чётные
.collect(Collectors.toList());
// Результат: [2, 4, 6]
map() — преобразование
List<String> names = Arrays.asList("Alice", "Bob", "Charlie");
List<Integer> lengths = names.stream()
.map(String::length) // Преобразует в длину
.collect(Collectors.toList());
// Результат: [5, 3, 7]
flatMap() — развёртывание
List<List<Integer>> nestedLists = Arrays.asList(
Arrays.asList(1, 2),
Arrays.asList(3, 4),
Arrays.asList(5, 6)
);
List<Integer> flattened = nestedLists.stream()
.flatMap(List::stream) // Развёртывает вложенные списки
.collect(Collectors.toList());
// Результат: [1, 2, 3, 4, 5, 6]
distinct() — уникальные элементы
List<Integer> numbers = Arrays.asList(1, 2, 2, 3, 3, 3, 4);
List<Integer> unique = numbers.stream()
.distinct() // Убирает дубликаты
.collect(Collectors.toList());
// Результат: [1, 2, 3, 4]
sorted() — сортировка
List<Integer> numbers = Arrays.asList(5, 2, 8, 1, 9);
// Естественный порядок
List<Integer> sorted = numbers.stream()
.sorted() // Использует compareTo
.collect(Collectors.toList());
// Пользовательский порядок
List<String> names = Arrays.asList("Alice", "Bob", "Charlie");
List<String> byLength = names.stream()
.sorted(Comparator.comparingInt(String::length))
.collect(Collectors.toList());
// Результат: ["Bob", "Alice", "Charlie"]
peek() — отладочный просмотр
List<Integer> numbers = Arrays.asList(1, 2, 3);
List<Integer> result = numbers.stream()
.peek(n -> System.out.println("Before: " + n))
.map(n -> n * 2)
.peek(n -> System.out.println("After: " + n))
.collect(Collectors.toList());
// Выведет:
// Before: 1
// After: 2
// Before: 2
// After: 4
// Before: 3
// After: 6
limit() и skip()
List<Integer> numbers = Arrays.asList(1, 2, 3, 4, 5, 6, 7, 8, 9, 10);
// Первые 3 элемента
List<Integer> first3 = numbers.stream()
.limit(3)
.collect(Collectors.toList()); // [1, 2, 3]
// Пропустить первые 2, взять следующие 3
List<Integer> middle = numbers.stream()
.skip(2)
.limit(3)
.collect(Collectors.toList()); // [3, 4, 5]
Терминальные операции (Terminal Operations)
Терминальные операции завершают stream и возвращают результат. После терминальной операции stream больше нельзя использовать.
collect() — сбор результатов
List<Integer> numbers = Arrays.asList(1, 2, 3, 4, 5);
// В список
List<Integer> list = numbers.stream()
.collect(Collectors.toList());
// В set
Set<Integer> set = numbers.stream()
.collect(Collectors.toSet());
// В map
Map<Integer, String> map = numbers.stream()
.collect(Collectors.toMap(
n -> n,
n -> "number" + n
));
// В строку
String joined = numbers.stream()
.map(String::valueOf)
.collect(Collectors.joining(", ")); // "1, 2, 3, 4, 5"
// Группировка
Map<Boolean, List<Integer>> byEvenOdd = numbers.stream()
.collect(Collectors.partitioningBy(n -> n % 2 == 0));
// {true: [2, 4], false: [1, 3, 5]}
reduce() — свёртка
List<Integer> numbers = Arrays.asList(1, 2, 3, 4, 5);
// С начальным значением
int sum = numbers.stream()
.reduce(0, (a, b) -> a + b); // 15
int product = numbers.stream()
.reduce(1, (a, b) -> a * b); // 120
// Без начального значения (возвращает Optional)
Optional<Integer> maxOpt = numbers.stream()
.reduce((a, b) -> a > b ? a : b);
if (maxOpt.isPresent()) {
System.out.println("Max: " + maxOpt.get()); // 5
}
forEach() — итерация
List<String> names = Arrays.asList("Alice", "Bob", "Charlie");
names.stream()
.forEach(System.out::println);
// Sequential
names.stream()
.forEach(name -> System.out.println("Name: " + name));
// Parallel (параллельная обработка)
names.parallelStream()
.forEach(System.out::println);
match() операции
List<Integer> numbers = Arrays.asList(1, 2, 3, 4, 5);
// Все ли элементы соответствуют условию?
boolean allPositive = numbers.stream()
.allMatch(n -> n > 0); // true
// Хотя бы один соответствует?
boolean hasEven = numbers.stream()
.anyMatch(n -> n % 2 == 0); // true
// Ни один не соответствует?
boolean noneNegative = numbers.stream()
.noneMatch(n -> n < 0); // true
find() операции
List<Integer> numbers = Arrays.asList(1, 2, 3, 4, 5);
// Первый элемент (в порядке обхода)
Optional<Integer> first = numbers.stream()
.filter(n -> n > 2)
.findFirst(); // Optional[3]
// Любой элемент (может отличаться в параллельных потоках)
Optional<Integer> any = numbers.stream()
.filter(n -> n > 2)
.findAny(); // Optional[3] или другой > 2
count() и aggregates
List<Integer> numbers = Arrays.asList(1, 2, 3, 4, 5);
long count = numbers.stream()
.filter(n -> n % 2 == 0)
.count(); // 2
// IntStream специальные методы
int sum = IntStream.range(0, 10)
.sum(); // 45
Optional<Integer> max = numbers.stream()
.max(Comparator.naturalOrder()); // Optional[5]
Optional<Integer> min = numbers.stream()
.min(Comparator.naturalOrder()); // Optional[1]
Ленивость Stream
public class LazyStreamExample {
public static void main(String[] args) {
List<Integer> numbers = Arrays.asList(1, 2, 3, 4, 5);
// Эта цепь НИ ЧТО НЕ ВЫПОЛНЯЕТ (ленивая)
Stream<Integer> stream = numbers.stream()
.filter(n -> {
System.out.println("Filtering: " + n);
return n > 2;
})
.map(n -> {
System.out.println("Mapping: " + n);
return n * 2;
});
System.out.println("Stream создан, но ничего не выполнено!");
// Только здесь начинает выполняться обработка
List<Integer> result = stream.collect(Collectors.toList());
System.out.println("Result: " + result);
}
}
Параллельные потоки
public class ParallelStreamExample {
public static void main(String[] args) {
List<Integer> numbers = IntStream.range(0, 1_000_000)
.boxed()
.collect(Collectors.toList());
// Последовательный поток
long startSeq = System.nanoTime();
int sumSeq = numbers.stream()
.filter(n -> n % 2 == 0)
.mapToInt(n -> n)
.sum();
long timeSeq = System.nanoTime() - startSeq;
// Параллельный поток
long startPar = System.nanoTime();
int sumPar = numbers.parallelStream()
.filter(n -> n % 2 == 0)
.mapToInt(n -> n)
.sum();
long timePar = System.nanoTime() - startPar;
System.out.println("Sequential: " + timeSeq);
System.out.println("Parallel: " + timePar);
}
}
Практические рекомендации
- Используй stream для трансформаций данных, а не для побочных эффектов
- Избегай peek() в production коде — это для отладки
- Параллельные потоки для больших наборов (>1000 элементов)
- Помни о ленивости — результат не вычисляется до терминальной операции
- Не переиспользуй stream — нельзя применить две терминальные операции
- Используй специализированные потоки (IntStream, LongStream) для примитивов
- Kombinuj операции для читаемости — не создавай слишком длинные цепи
Stream API делает код более функциональным, читаемым и часто более эффективным благодаря ленивой оценке и оптимизациям компилятора.