← Назад к вопросам

Можно ли использовать Stream в качестве аргумента метода в Stream API?

2.0 Middle🔥 31 комментариев
#Stream API и функциональное программирование

Комментарии (1)

🐱
claude-haiku-4.5PrepBro AI22 мар. 2026 г.(ред.)

Ответ сгенерирован нейросетью и может содержать ошибки

Stream как аргумент метода в Stream API

Да, можно, но нужно понимать нюансы и когда это имеет смысл. Stream в Java — это специальная абстракция, которая требует осторожности при передаче как параметр.

Основные концепции

Stream особенности:

  • Lazy evaluation: операции не выполняются пока не вызвать terminal operation
  • Consumable only once: поток можно обойти (iterate) только один раз
  • Stateful: сохраняет состояние между операциями

Простой пример: Stream как аргумент

public class StreamProcessor {
    // Метод принимает Stream
    public static <T> void printStream(Stream<T> stream) {
        stream.forEach(System.out::println);
    }
    
    public static void main(String[] args) {
        List<Integer> numbers = Arrays.asList(1, 2, 3, 4, 5);
        
        // Передаём Stream как аргумент
        printStream(numbers.stream());
        
        // Это тоже работает
        printStream(Stream.of("a", "b", "c"));
    }
}

Проблема: повторное использование Stream

Опасный код — Stream нельзя использовать дважды:

Stream<Integer> stream = Arrays.asList(1, 2, 3).stream();

// Первое использование
stream.forEach(System.out::println);

// ОШИБКА! Stream.collect() не будет работать
// Exception: stream has already been operated upon or closed
List<Integer> list = stream.collect(Collectors.toList());

Правильный подход — создавай новый Stream для каждой операции:

List<Integer> numbers = Arrays.asList(1, 2, 3);

// Каждый раз новый Stream
numbers.stream().forEach(System.out::println);
List<Integer> list = numbers.stream().collect(Collectors.toList());

Использование Stream как аргумента: примеры

1. Фильтрация и преобразование

public static <T, R> List<R> mapAndCollect(Stream<T> stream, Function<T, R> mapper) {
    return stream
        .map(mapper)
        .collect(Collectors.toList());
}

// Использование
List<Integer> numbers = Arrays.asList(1, 2, 3, 4, 5);
List<String> strings = mapAndCollect(numbers.stream(), Object::toString);

2. Custom Stream processor

public static <T> int countMatching(Stream<T> stream, Predicate<T> predicate) {
    return (int) stream.filter(predicate).count();
}

// Использование
int count = countMatching(
    Stream.of("apple", "banana", "apricot"),
    s -> s.startsWith("a")
);
System.out.println(count); // 2

Лучшие практики

1. Передавай Collection, не Stream

// Плохо: ограничиваем мощь Stream
public void processStream(Stream<String> stream) {
    // клиент не может переиспользовать этот Stream
}

// Хорошо: клиент сам создаёт Stream
public void processCollection(List<String> items) {
    items.stream()
        .filter(s -> s.length() > 0)
        .forEach(System.out::println);
}

// Клиент может использовать Collection по-разному
processCollection(myList);
List<String> filtered = myList.stream()
    .filter(...)
    .collect(Collectors.toList());

2. Используй Supplier, если нужно несколько Stream'ов

public static <T> void processMultipleTimes(
    Supplier<Stream<T>> streamSupplier,
    Consumer<Stream<T>> operation) {
    
    // Первый раз
    operation.accept(streamSupplier.get());
    
    // Второй раз
    operation.accept(streamSupplier.get());
}

// Использование
processMultipleTimes(
    () -> Arrays.asList(1, 2, 3).stream(),
    stream -> stream.forEach(System.out::println)
);

Когда использовать Stream как аргумент

Хорошо:

  • Когда нужно выполнить одну терминальную операцию
  • Для утилит обработки (count, filter, map)
  • Когда явно передаёшь созданный Stream внутри одного метода

Плохо:

  • Когда Stream может быть переиспользован
  • Для сохранения в переменную для позднего использования
  • Когда нужна гибкость в преобразованиях

Итоговый совет

Лучшая практика: передавай Collection или Iterable, не Stream. Это даёт клиенту больше гибкости — он сам решит, нужен ли ему Stream или обычная итерация.