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

Что вернется, при запуске фильтра в stream у ArrayList

1.2 Junior🔥 131 комментариев
#Stream API и функциональное программирование

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

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

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

Что возвращает filter() в Stream для ArrayList

Краткий ответ

filter() возвращает новый Stream, не ArrayList. Это промежуточная операция, которая создаёт новый поток с отфильтрованными элементами.

Визуализация

ArrayList<Integer> list = new ArrayList<>(Arrays.asList(1, 2, 3, 4, 5));

// filter() возвращает Stream, НЕ ArrayList
Stream<Integer> filteredStream = list.stream()
    .filter(n -> n > 2);

// Тип: Stream<Integer>, не ArrayList<Integer>
System.out.println(filteredStream.getClass());  
// java.util.stream.ReferencePipeline$2

Детальное объяснение

Процесс:

// Шаг 1: Создаём stream из ArrayList
Stream<Integer> stream1 = list.stream();  // Stream<Integer>

// Шаг 2: Применяем filter (промежуточная операция)
Stream<Integer> stream2 = stream1.filter(n -> n > 2);  // Stream<Integer>
// ВАЖНО: Никакой фильтрации не произошло! (lazy evaluation)

// Шаг 3: Применяем терминальную операцию
List<Integer> result = stream2.collect(Collectors.toList());  // ArrayList<Integer>
// ТЕПЕРЬ произойдёт фильтрация и создание нового ArrayList

Что происходит внутренне

Внутренняя структура Stream Pipeline:

public class ReferencePipeline<P_IN, P_OUT> extends AbstractPipeline<P_IN, P_OUT, Stream<P_OUT>> {
    // filter() возвращает новый ReferencePipeline
    @Override
    public final Stream<P_OUT> filter(Predicate<? super P_OUT> predicate) {
        return new StatelessOp<P_OUT, P_OUT>(this, StreamShape.REFERENCE,
                                              StreamOpFlag.NOT_SIZED) {
            @Override
            public Sink<P_OUT> opWrapSink(int flags, Sink<P_OUT> sink) {
                return new Sink.ChainedReference<P_OUT, P_OUT>(sink) {
                    @Override
                    public void accept(P_OUT u) {
                        if (predicate.test(u))  // Проверяем условие
                            downstream.accept(u);  // Передаём дальше
                    }
                };
            }
        };
    }
}

Это не ArrayList, это pipeline, который описывает как обрабатывать элементы.

Ленивость (Lazy Evaluation)

Критическое понимание:

ArrayList<Integer> list = new ArrayList<>(Arrays.asList(1, 2, 3, 4, 5));

// ❌ Это НИЧЕГО не фильтрует!
Stream<Integer> filtered = list.stream()
    .filter(n -> {
        System.out.println("Filtering " + n);
        return n > 2;
    });  // Ничего не напечатано!

// ✅ Фильтрация происходит ТУТ (терминальная операция)
List<Integer> result = filtered.collect(Collectors.toList());

// Вывод:
// Filtering 1
// Filtering 2
// Filtering 3
// Filtering 4
// Filtering 5

// result теперь: [3, 4, 5]

Полная цепь: от ArrayList к List

ArrayList<Integer> original = new ArrayList<>(Arrays.asList(1, 2, 3, 4, 5));

// 1. Получаем Stream<Integer>
Stream<Integer> step1 = original.stream();

// 2. filter() возвращает Stream<Integer> (промежуточная)
Stream<Integer> step2 = step1.filter(n -> n > 2);

// 3. map() возвращает Stream<String> (промежуточная)
Stream<String> step3 = step2.map(n -> "Number: " + n);

// 4. collect() возвращает List<String> (терминальная)
List<String> result = step3.collect(Collectors.toList());

// Результат: ["Number: 3", "Number: 4", "Number: 5"]
// Тип: ArrayList (стандартный результат collect)

Разные способы закончить filter()

1. collect() → List

List<Integer> list = arr.stream()
    .filter(n -> n > 2)
    .collect(Collectors.toList());  // ← List<Integer>

2. toArray() → массив

Integer[] array = arr.stream()
    .filter(n -> n > 2)
    .toArray(Integer[]::new);  // ← Integer[]

3. findFirst() → Optional

Optional<Integer> first = arr.stream()
    .filter(n -> n > 2)
    .findFirst();  // ← Optional<Integer>

4. count() → long

long count = arr.stream()
    .filter(n -> n > 2)
    .count();  // ← long

5. forEach() → void

arr.stream()
    .filter(n -> n > 2)
    .forEach(System.out::println);  // ← void

Важное: filter() не модифицирует оригинал

Исходный ArrayList остаётся неизменным:

ArrayList<Integer> original = new ArrayList<>(Arrays.asList(1, 2, 3, 4, 5));

List<Integer> filtered = original.stream()
    .filter(n -> n > 2)
    .collect(Collectors.toList());

System.out.println("Original: " + original);  // [1, 2, 3, 4, 5]
System.out.println("Filtered: " + filtered);  // [3, 4, 5]

// Создался НОВЫЙ список, оригинал не изменился

Выхлопные типы

Чтобы получитьИспользуйПример
Stream.filter() → .filter()stream.filter(...).filter(...)
List.collect(toList())stream.filter(...).collect(Collectors.toList())
Set.collect(toSet())stream.filter(...).collect(Collectors.toSet())
Map.collect(toMap())stream.filter(...).collect(Collectors.toMap(...))
Array.toArray()stream.filter(...).toArray(Integer[]::new)
Single.findFirst(), .findAny()stream.filter(...).findFirst()
Count.count()stream.filter(...).count()
Boolean.anyMatch(), .allMatch()stream.filter(...).anyMatch(...)

Практический пример

public List<User> getActiveAdminUsers(List<User> users) {
    // filter() возвращает Stream<User>
    // collect() преобразует Stream в List
    return users.stream()
        .filter(User::isActive)              // Stream<User>
        .filter(user -> user.getRole() == Role.ADMIN)  // Stream<User>
        .collect(Collectors.toList());       // List<User>
}

// Этот список новый, он не влияет на исходный 'users'

Частая ошибка

// ❌ Ошибка: забыли терминальную операцию
list.stream()
    .filter(n -> n > 2);  // НИЧЕГО не произойдёт!

// Правильно:
List<Integer> result = list.stream()
    .filter(n -> n > 2)
    .collect(Collectors.toList());  // Теперь создаст новый список

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

filter() возвращает:

  • Тип: Stream<T> (промежуточная операция)
  • Что происходит: Ничего! (lazy evaluation)
  • Когда возвращается нечто полезное: После терминальной операции (collect, count, findFirst, etc)
  • Оригинальный ArrayList: Остаётся неизменным