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

В чем разница между коллекцией и стримом?

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, Mapfilter().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 — для их трансформации и обработки.

В чем разница между коллекцией и стримом? | PrepBro