Чем collect отличается от reduce при сборке стрима?
- Чем collect отличается от reduce и какие ловушки есть у Collectors.toMap и groupingBy?
reduce — неизменяемая свёртка: из двух значений получается новое (сумма, максимум). collect — изменяемая свёртка: элементы накапливаются в один контейнер (список, карта, StringBuilder) через Collector. Коллекции и строки собирают только collect — reduce создавал бы новый объект на каждом шаге.
- Ловушки toMap: дубликат ключа — IllegalStateException, null-значение — NPE
- groupingBy по умолчанию HashMap и List; downstream-коллектор меняет агрегат
- Для parallel: reduce требует ассоциативной функции и нейтрального элемента
А ещё Простые агрегаты: sum, count, max у примитивных стримов
Подробно
reduce:
int total = orders.stream().map(Order::sum).reduce(0, Integer::sum);
identity — нейтральный элемент, функция — ассоциативная. Подходит для значений. Собирать так строку или список — на каждом шаге новый объект, сложность квадратичная.
collect:
Map<Dept, List<Emp>> byDept = emps.stream()
.collect(Collectors.groupingBy(Emp::dept));
String csv = names.stream().collect(Collectors.joining(","));
Collector описывает поставщика контейнера, функцию добавления элемента и функцию слияния контейнеров (для parallel).
Ловушки Collectors:
toMapбез функции слияния падает на дубликате ключа (IllegalStateException: Duplicate key);toMapпадает на null-значении (NPE) — внутри используетсяHashMap.merge;groupingByкладёт в HashMap — порядок групп не определён; нужен порядок —groupingBy(f, TreeMap::new, toList());toList()у Stream (Java 16) неизменяемый,Collectors.toList()— изменяемый ArrayList без гарантии.
Что хотят услышать
- Неизменяемая и изменяемая свёртка.
- Почему коллекции собирают collect.
- Ловушки toMap и groupingBy.
Частые ошибки
- Собирать список через reduce с пересозданием контейнера.
- Думать, что reduce умеет mutable-накопление.
Проверьте себя
Один верный вариант. Подумайте, потом откройте ответ.
Показать варианты и ответ
- ✗Они идентичны; collect — устаревший псевдоним reduce
- ✗reduce собирает в коллекцию, collect — в одно значение
- ✗collect работает только последовательно, а reduce — только в параллельном стриме
- ✓reduce — неизменяемая свёртка в значение, collect — в изменяемый контейнер
Что спросят дальше
- Что такое identity в reduce?
- Зачем коллектору ассоциативность для параллели?
- Что означают характеристики коллектора UNORDERED и CONCURRENT?
- Когда параллельный стрим действительно даёт выигрыш?
Разбор темы
- Collectors и свёртка
reduce против collect, базовые коллекторы и ловушка toMap, группировка и downstream-коллекторы, характеристики и параллельность.
Связанные вопросы
- Что произойдёт в Collectors.toMap, если два элемента дают одинаковый ключ, а merge-функция не передана? Middle
- Функция значения в Collectors.toMap вернула null для одного из элементов. Что произойдёт? Middle
- Как одним коллектором получить «количество элементов в каждой группе»? Middle
- Что делает терминальная операция collect? Junior
Разбор собеседования с sotto
После собеседования sotto разбирает разговор: оценка, каждый вопрос — хорошо, частично или слабо, чего не хватило в ответе и над чем поработать к следующему разу. Первые 30 минут бесплатно.