Как получить из списка объектов коллекцию значений одного поля с помощью Stream API в Java?
Что проверяют
Базовое владение Stream API: map плюс терминальная операция, и понимание, какая коллекция получится на выходе. Следом спрашивают про изменяемость результата, дубликаты и null, иногда просят собрать Map вместо списка.
Как отвечать
Каркас в одну строку:
List<String> names = people.stream().map(Person::getName).toList();Дальше важны детали, на которых и строится разговор:
Stream.toList(): появился в Java 16, возвращает неизменяемый список, попыткаaddброситUnsupportedOperationException.nullпри этом допускается;Collectors.toList(): старый способ, спецификация не гарантирует ни тип, ни изменяемость, хотя на практике этоArrayList. Если нужен именно изменяемый список, пишитеCollectors.toCollection(ArrayList::new);Collectors.toUnmodifiableList(): с Java 10, неизменяемый, но наnullбросаетNullPointerException;Collectors.toSet(): если нужны уникальные значения, аtoCollection(TreeSet::new)даст их сразу отсортированными;Collectors.toMap(Person::getId, Person::getName): для пары ключ и значение. На повторяющемся ключе броситIllegalStateException, поэтому третьим аргументом передают функцию слияния;mapToInt(Person::getAge): для числовых полей, чтобы не упаковывать каждое значение вIntegerи сразу получитьsum()илиaverage().
Если объекты пришли из базы через JPA, стоит добавить: одну колонку дешевле вытащить запросом select p.name from Person p, чем загружать сущности целиком ради одного поля.
Где спотыкаются
Меняют результат toList() и получают исключение в рантайме. Разница между toList() и Collectors.toList() сейчас один из самых частых уточняющих вопросов.
Забывают про null. Если у части объектов поле пустое, toMap упадёт с NullPointerException на значении, а sorted() на сравнении. Нужен filter(Objects::nonNull) после map.
Строят поток без терминальной операции и ждут результата. Стримы ленивые: пока не вызван toList, collect или forEach, ни одна лямбда не выполнится.