← Назад к вопросам
Что вернется, при запуске фильтра в 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: Остаётся неизменным