Можно ли удалить элемент из исходной коллекции с помощью стрима
Что проверяют
Знаете ли вы требование невмешательства в источник и понимаете ли, что стрим это конвейер чтения, а не способ править коллекцию. Вопрос отсеивает тех, кто выучил синтаксис стримов, но не читал их контракт.
Как отвечать
Нет. Стрим не меняет источник, и более того, менять источник во время работы стрима запрещено: это требование невмешательства из документации пакета. Коллекции из JDK обычно отвечают на нарушение ConcurrentModificationException, но гарантии нет, возможен и тихо неверный результат, что хуже исключения.
Рабочих вариантов три: removeIf с предикатом, явный Iterator с его методом remove, либо сборка новой коллекции и замена ссылки.
numbers.removeIf(n -> n % 2 == 0);
List<Integer> odd = numbers.stream()
.filter(n -> n % 2 != 0)
.toList();Стоит оговорить, что removeIf требует изменяемую коллекцию: на результате List.of он бросит UnsupportedOperationException, на Arrays.asList тоже, там фиксирован размер. И что toList возвращает неизменяемый список, а изменяемый даёт collect(Collectors.toCollection(ArrayList::new)).
Где спотыкаются
Пишут list.stream().filter(...).forEach(list::remove). Это ровно то самое вмешательство: либо исключение, либо пропуск элементов из-за сдвига индексов.
Считают, что filter удаляет. Он только не пропускает элемент дальше по конвейеру и к источнику не прикасается. Заодно забывают про ленивость: без терминальной операции цепочка не выполнится вовсе, и код без collect или forEach просто ничего не делает.
Путают неизменность коллекции с неизменностью элементов. Состав стрим не трогает, а вот лямбда внутри map или peek вполне может менять состояние самих объектов, и на параллельном стриме это превращается в гонку.