← Назад к вопросам
В чем разница между коллекцией и стримом?
1.7 Middle🔥 251 комментариев
#Stream API и функциональное программирование#Коллекции#Основы Java
Комментарии (1)
🐱
claude-haiku-4.5PrepBro AI22 мар. 2026 г.(ред.)
Ответ сгенерирован нейросетью и может содержать ошибки
Разница между коллекцией и Stream
Коллекции и потоки (Stream) — два фундаментально разных способа работы с данными в Java. Хотя они часто используются вместе, их природа и применение существенно отличаются.
Коллекция: контейнер данных
Коллекция — это контейнер объектов в памяти, который хранит данные и предоставляет способы их доступа и модификации.
// Коллекция — это объект, содержащий данные
List<Integer> numbers = Arrays.asList(1, 2, 3, 4, 5);
Set<String> names = new HashSet<>(Arrays.asList("Alice", "Bob"));
Map<String, Integer> ages = new HashMap<>();
Характеристики:
- Хранит в памяти — все элементы присутствуют одновременно
- Изменяемость — можно добавлять, удалять, модифицировать элементы
- Многократный доступ — можно итерировать несколько раз
- Размер известен — можно получить количество элементов
- Конкретные структуры — List (упорядоченная), Set (уникальные), Map (ключ-значение)
Stream: конвейер обработки
Stream — это абстрактная последовательность данных, которая описывает трансформации, но не хранит сами данные.
// Stream — это описание операций, а не сами данные
List<Integer> numbers = Arrays.asList(1, 2, 3, 4, 5);
Stream<Integer> stream = numbers.stream()
.filter(n -> n > 2)
.map(n -> n * 2);
// Stream ничего не делает, пока мы не вызовем terminal операцию
List<Integer> result = stream.collect(Collectors.toList());
Характеристики:
- Не хранит данные — только описывает операции
- Ленивое вычисление — операции выполняются при необходимости
- Однократный проход — Stream после terminal операции исчерпывается
- Функциональный подход — нет изменений, только трансформации
- Бесконечные последовательности — Stream.generate(), Stream.iterate()
Сравнение
// === КОЛЛЕКЦИЯ ===
List<Integer> numbers = Arrays.asList(1, 2, 3, 4, 5);
// Хранит данные в памяти
System.out.println(numbers.size()); // 5
System.out.println(numbers.get(0)); // 1
// Можно итерировать много раз
for (Integer n : numbers) System.out.println(n);
for (Integer n : numbers) System.out.println(n); // второй раз работает
// Можно модифицировать
numbers.add(6);
numbers.remove(0);
// === STREAM ===
Stream<Integer> stream = numbers.stream()
.filter(n -> n > 2)
.map(n -> n * 2);
// Не хранит данные, только описывает операции
// Промежуточные операции (filter, map) ничего не делают
// Terminal операция запускает вычисления
List<Integer> result = stream.collect(Collectors.toList());
// После terminal операции Stream исчерпан
// stream.forEach(...); // Ошибка! Stream already operated upon
Промежуточные vs Terminal операции
Промежуточные операции:
- filter, map, flatMap, sorted, distinct, limit, skip
- Возвращают Stream
- Ленивые — не выполняются, пока не вызвана terminal операция
Stream<Integer> lazy = numbers.stream()
.filter(n -> n > 2) // еще не выполнен
.map(n -> n * 2); // еще не выполнен
// в этом месте ничего не произошло!
Terminal операции:
- collect, forEach, reduce, count, findFirst, anyMatch
- Возвращают результат (не Stream)
- Эгер — запускают все промежуточные операции
List<Integer> result = lazy.collect(Collectors.toList()); // Теперь запустилось!
Визуальное сравнение
| Аспект | Коллекция | Stream |
|---|---|---|
| Тип | Контейнер данных | Конвейер обработки |
| Хранилище | В памяти | Не хранит |
| Размер | Известен | Может быть бесконечным |
| Использование | Доступ, модификация | Трансформация |
| Многократный доступ | ✓ | ✗ (только 1 раз) |
| Изменяемость | ✓ | ✗ (функциональный) |
| Ленивость | Нет | ✓ (промежуточные) |
| Параллелизм | Нет | ✓ (parallelStream) |
| Пример | List, Set, Map | filter().map().collect() |
Когда использовать что
Используй коллекции:
- Нужно хранить данные
- Нужен повторный доступ
- Нужна модификация (добавление, удаление)
- Нужно узнать размер
- Простые операции
List<User> users = new ArrayList<>();
users.add(new User("Alice"));
users.add(new User("Bob"));
Используй Stream:
- Нужна трансформация данных
- Многоступенчатая обработка
- Параллельная обработка
- Работа с функциональными операциями
- Бесконечные последовательности
users.stream()
.filter(User::isActive)
.map(User::getName)
.sorted()
.forEach(System.out::println);
Практический пример
// Хорошая практика: коллекция как источник, Stream как трубопровод
List<String> data = Arrays.asList("apple", "banana", "cherry");
// Stream из коллекции
List<String> result = data.stream()
.filter(s -> s.length() > 5)
.map(String::toUpperCase)
.collect(Collectors.toList()); // Коллекция результата
Вывод: коллекции — для хранения и доступа к данным, Stream — для их трансформации и обработки.