Что такое конвейерный метод в Stream?
Комментарии (1)
Ответ сгенерирован нейросетью и может содержать ошибки
Конвейерный метод в Stream API
Конвейерный метод это метод в Stream API, который возвращает новый Stream, позволяя вызывать следующий метод в цепи. Это основа функционального программирования в Java.
Определение
Конвейерный метод (Intermediate operation):
- Возвращает Stream (не завершает обработку)
- Позволяет продолжить цепь вызовов
- Работает лениво (вычисления отложены)
- Позволяет написать gluent interface код
List<Integer> numbers = Arrays.asList(1, 2, 3, 4, 5);
// Каждый метод возвращает Stream
numbers.stream() // Stream<Integer>
.filter(x -> x > 2) // Stream<Integer> (конвейерный)
.map(x -> x * 2) // Stream<Integer> (конвейерный)
.forEach(System.out::println); // void (терминальный)
Список конвейерных методов
1. filter() — отфильтровать элементы
Stream<Integer> stream = numbers.stream()
.filter(x -> x > 2); // Возвращает Stream
2. map() — преобразовать элементы
Stream<String> stream = numbers.stream()
.map(x -> "number: " + x); // Возвращает Stream
3. flatMap() — развернуть вложенные потоки
Stream<Integer> stream = numbers.stream()
.flatMap(n -> Stream.of(n, n * 2)); // Возвращает Stream
4. distinct() — удалить дубликаты
Stream<Integer> stream = numbers.stream()
.distinct(); // Возвращает Stream
5. sorted() — отсортировать
Stream<Integer> stream = numbers.stream()
.sorted(); // Возвращает Stream
6. limit() — ограничить количество
Stream<Integer> stream = numbers.stream()
.limit(3); // Возвращает Stream с максимум 3 элементами
7. skip() — пропустить количество
Stream<Integer> stream = numbers.stream()
.skip(2); // Пропустить первые 2 элемента
8. peek() — выполнить действие без изменений
Stream<Integer> stream = numbers.stream()
.peek(x -> System.out.println("Processing: " + x));
Пример конвейерной цепи
List<Integer> numbers = Arrays.asList(1, 2, 3, 4, 5, 6);
List<Integer> result = numbers.stream()
.filter(x -> x > 2) // Конвейерный: 3, 4, 5, 6
.map(x -> x * 2) // Конвейерный: 6, 8, 10, 12
.limit(2) // Конвейерный: 6, 8
.collect(Collectors.toList()); // Терминальный
System.out.println(result); // [6, 8]
Ленивые вычисления (Lazy Evaluation)
Конвейерные методы работают лениво. Вычисления происходят только когда вызвали терминальный метод.
List<Integer> numbers = Arrays.asList(1, 2, 3, 4, 5);
// На этом этапе НИЧЕГО не вычисляется
Stream<Integer> stream = numbers.stream()
.filter(x -> {
System.out.println("Filtering " + x);
return x > 2;
})
.map(x -> {
System.out.println("Mapping " + x);
return x * 2;
});
System.out.println("Stream created (nothing computed yet)");
// СЕЙЧАС произойдут вычисления (терминальный метод)
stream.forEach(System.out::println);
Вывод:
Stream created (nothing computed yet)
Filtering 1
Filtering 2
Filtering 3
Mapping 3
6
Filtering 4
Mapping 4
8
Filtering 5
Mapping 5
10
Конвейерный vs Терминальный
// Конвейерный — возвращает Stream
Stream<Integer> stream = numbers.stream()
.filter(x -> x > 2);
// Можно продолжить:
stream.map(x -> x * 2);
// Терминальный — возвращает результат
List<Integer> list = numbers.stream()
.filter(x -> x > 2)
.collect(Collectors.toList()); // List, не Stream!
// Нельзя продолжить, stream закрыт
Терминальные методы (для полноты)
// Все эти методы ЗАКАНЧИВАЮТ цепь
numbers.stream().forEach(System.out::println); // void
numbers.stream().collect(Collectors.toList()); // List
numbers.stream().reduce(0, Integer::sum); // Integer
numbers.stream().count(); // long
numbers.stream().max(Integer::compare); // Optional
numbers.stream().anyMatch(x -> x > 5); // boolean
Плюсы конвейерных методов
1. Читаемость кода
// Конвейерный подход — понятно что происходит
List<String> result = users.stream()
.filter(u -> u.isActive())
.map(User::getName)
.sorted()
.collect(Collectors.toList());
// vs Императивный подход
List<String> result = new ArrayList<>();
for (User u : users) {
if (u.isActive()) {
result.add(u.getName());
}
}
Collections.sort(result);
2. Производительность (благодаря лениости)
// Обработает максимум 2 элемента (limit прерывает)
numbers.stream()
.filter(x -> x % 2 == 0) // Даже если есть млн элементов
.limit(2)
.forEach(System.out::println);
3. Параллелизм
// Просто меняем stream() на parallelStream()
numbers.parallelStream()
.filter(x -> x > 2)
.map(x -> x * 2)
.collect(Collectors.toList());
// Параллельная обработка на всех потоках
Практический пример: обработка данных
public class StreamExample {
public static void main(String[] args) {
List<Person> people = Arrays.asList(
new Person("Alice", 30, "Engineer"),
new Person("Bob", 25, "Manager"),
new Person("Charlie", 35, "Engineer"),
new Person("David", 28, "Designer")
);
// Конвейерная цепь
List<String> engineers = people.stream()
.filter(p -> "Engineer".equals(p.getRole())) // Конвейерный
.filter(p -> p.getAge() > 25) // Конвейерный
.map(Person::getName) // Конвейерный
.sorted() // Конвейерный
.collect(Collectors.toList()); // Терминальный
System.out.println(engineers); // [Alice, Charlie]
}
}
class Person {
private String name;
private int age;
private String role;
// getters...
}
Итог
Конвейерный метод в Stream:
- Возвращает Stream для продолжения цепи
- Работает лениво (отложенные вычисления)
- Примеры: filter, map, sorted, distinct, limit, skip
- Позволяет писать функциональный код на Java
- Оптимизирует производительность за счёт short-circuit операций
Конвейерные методы — это сердце Stream API в Java, позволяя писать декларативный, читаемый и эффективный код.