Как оставить уникальные объекты по полю
Короткий ответ
distinct() сравнивает объекты целиком. Чтобы сравнивать по одному полю, запоминайте уже встреченные значения в Set:
record Order(int id, String customer) {}
void main() {
var orders = List.of(new Order(1, "Anna"), new Order(2, "Oleg"), new Order(3, "Anna"));
var seen = new HashSet<String>();
var firstPerCustomer = orders.stream().filter(o -> seen.add(o.customer())).toList();
IO.println(firstPerCustomer);
}Результат:
[Order[id=1, customer=Anna], Order[id=2, customer=Oleg]]seen.add() возвращает false для повторного значения, и такой элемент отфильтровывается.
Через toMap
Способ без внешнего состояния: собрать Map по полю и взять значения:
record Order(int id, String customer) {}
void main() {
var orders = List.of(new Order(1, "Anna"), new Order(2, "Oleg"), new Order(3, "Anna"));
var lastPerCustomer = orders.stream()
.collect(Collectors.toMap(Order::customer, o -> o, (first, second) -> second, LinkedHashMap::new))
.values();
IO.println(lastPerCustomer);
}Результат:
[Order[id=3, customer=Anna], Order[id=2, customer=Oleg]]Через TreeSet
TreeSet с компаратором по полю считает объекты с одинаковым полем равными:
record Order(int id, String customer) {}
void main() {
var orders = List.of(new Order(1, "Oleg"), new Order(2, "Anna"), new Order(3, "Oleg"));
var unique = new TreeSet<>(Comparator.comparing(Order::customer));
unique.addAll(orders);
IO.println(unique);
}Результат:
[Order[id=2, customer=Anna], Order[id=1, customer=Oleg]]Частая ошибка
Использовать приём с Set в параллельном потоке. HashSet не потокобезопасен, и результат становится непредсказуемым. В parallelStream() используйте вариант с toMap().
Функции из этого ответа
- Collectors.toMap() — Сборщик, который строит словарь из элементов потока.
- Stream.distinct() — Убирает повторяющиеся элементы потока.
- TreeSet — Множество, которое хранит элементы в отсортированном порядке.