В чем разница между Stream API и коллекциями в Java?

Ответ

Коллекция (например, List, Set) — это, прежде всего, структура данных в памяти для хранения и организации элементов.

Stream API — это абстракция для декларативной обработки последовательностей данных (которые могут поступать из коллекций, I/O, генераторов).

Ключевые различия:

Аспект Коллекция Stream API
Назначение Хранение и прямой доступ к данным Выполнение вычислений над данными
Модификация Позволяет добавлять/удалять элементы (мутабельность) Не изменяет источник данных, создает новый поток/результат
Вычисления Немедленные (eager) Отложенные (lazy), выполняются только при вызове терминальной операции
Итерация Внешняя (вы управляете циклом) Внутренняя (библиотека управляет итерацией)
Потребление Может быть использована многократно Одноразовый (после терминальной операции поток закрыт)
Параллелизм Требует ручной синхронизации Легкое распараллеливание через .parallelStream() или .parallel()
Возможности Ограничена методами структуры данных Богатый набор операций: filter, map, reduce, flatMap, groupingBy и др.

Пример:

List<String> names = Arrays.asList("Anna", "Bob", "Alice", "David"); // Коллекция

// Stream API для обработки
List<String> result = names.stream()          // Создание потока из коллекции
        .filter(name -> name.length() > 3)    // Промежуточная операция (lazy)
        .map(String::toUpperCase)             // Промежуточная операция (lazy)
        .sorted()                             // Промежуточная операция (lazy)
        .collect(Collectors.toList());        // Терминальная операция (eager)
// result = ["ALICE", "ANNA", "DAVID"]

Вывод: Коллекции — это про данные, Stream API — про операции над данными. Они дополняют друг друга: коллекции часто являются источником или приемником для потоков.

Ответ 18+ 🔞

Слушай, а вот объясни мне, как человеку, который только что из пещеры с дубиной вылез: в чём, блядь, разница между коллекцией и этим вашим Stream API? Ну, там List, Set — это же вроде тоже коллекции, да? А потом все вокруг как заведённые: «стрим, стрим, стрим»! Я один нихуя не понимаю, или это реально две разные вещи?

Так, поехали по-простому, а то голова уже болит.

Коллекция (List, Set и прочая муть) — это, грубо говоря, шкаф с хуями. Ну, то есть структура в памяти, куда ты складываешь свои данные, чтобы они там лежали. Можешь туда засунуть, вытащить, переставить — всё, как с обычным шкафом. Данные есть, они на месте, ты к ним можешь в любой момент подойти и потрогать.

Stream API — это уже конвейер для обработки этих самых хуёв. Ты не к шкафу идёшь, а берёшь и говоришь: «Так, слушай сюда, машина: вот из этого шкафа возьми все предметы, отфильтруй те, которые длиннее трёх букв, перекрась их в синий цвет, отсортируй по размеру и сложи мне в новую коробку». И ты даже не знаешь, как именно машина это делает — тебе похуй. Ты просто описал, что ты хочешь. Это абстракция, блядь, над операциями.

А теперь, сука, табличка, чтобы вообще мозг не взорвался:

Что сравниваем Коллекция (шкаф) Stream API (конвейер)
Зачем нужна? Хранить данные и давать к ним доступ. Выполнять вычисления над данными.
Можно менять? Да, конечно, добавляй/удаляй — шкаф твой. Не-а, источник данных не трогает. Он создаёт новый поток или результат.
Когда считает? Сразу, как попросил (жадный, как я в столовой). Отложенно, лениво. Пока не скажешь «всё, давай результат» — нихуя не делает.
Кто крутит циклы? Ты, своими кривыми ручками (внешняя итерация). Библиотека, внутри себя (внутренняя итерация). Ты только условия задаёшь.
Можно юзать много раз? Да, хоть сто раз открывай шкаф. Один раз, потом поток «закрывается». Как сигарета — потянул и выбросил.
Как распараллелить? Самому писать синхронизацию, а потом дебажить три недели. Один вызов .parallelStream() и поехали, ёпта!
Что умеет? То, что заложено в структуре: добавить, получить, удалить. Овердохуища операций: filter, map, reduce, flatMap, groupingBy — красота!

Ну и пример, чтобы окончательно проняло:

// Это наш шкаф (коллекция) с именами
List<String> names = Arrays.asList("Anna", "Bob", "Alice", "David");

// А это мы запускаем конвейер (Stream API) для обработки
List<String> result = names.stream()          // Берём поток из шкафа
        .filter(name -> name.length() > 3)    // Отфильтровываем короткие (лениво, ещё нихуя не делается)
        .map(String::toUpperCase)             // Переводим в верхний регистр (всё ещё лениво)
        .sorted()                             // Сортируем (опять лениво!)
        .collect(Collectors.toList());        // А вот тут уже — ВСЁ, ПАЦАНЫ, ЗАБИРАЕМ РЕЗУЛЬТАТ! (жадная операция)
// result = ["ALICE", "ANNA", "DAVID"]

Итог, блядь: Коллекции — это данные, Stream API — это инструкции, что с этими данными делать. Они не вместо друг друга, а вместе: из коллекции делаешь поток, обрабатываешь его, а результат обратно в коллекцию или куда тебе надо. Всё гениальное — просто, как три рубля.