7

Java 8: Уникальные элементы по свойству (Distinct by property)

2

Как в Java 8 отфильтровать коллекцию, используя API Stream, проверяя уникальность свойства каждого объекта?

У меня есть список объектов типа Person, и я хочу удалить людей с одинаковыми именами.

Используя метод:

persons.stream().distinct();

Я получаю стандартную проверку на равенство для объектов Person, поэтому мне нужно что-то вроде:

persons.stream().distinct(p -> p.getName());

К сожалению, метод distinct() не имеет такой перегрузки. Возможно ли сделать это кратко, не изменяя проверку на равенство внутри класса Person?

5 ответ(ов)

2

Альтернативным решением будет поместить объекты Person в карту, используя имя в качестве ключа:

persons.collect(Collectors.toMap(Person::getName, p -> p, (p, q) -> p)).values();

Обратите внимание, что в случае дублирования имени в карту будет помещён первый встреченный объект Person.

1

Вы можете обернуть объекты Person в другой класс, который будет сравнивать только имена этих людей. После этого вы сможете "разобрать" обертки, чтобы снова получить поток объектов Person. Операции со(stream) могут выглядеть следующим образом:

persons.stream()
    .map(Wrapper::new)
    .distinct()
    .map(Wrapper::unwrap)
    ...;

Класс Wrapper может выглядеть следующим образом:

class Wrapper {
    private final Person person;
    public Wrapper(Person person) {
        this.person = person;
    }
    public Person unwrap() {
        return person;
    }
    public boolean equals(Object other) {
        if (other instanceof Wrapper) {
            return ((Wrapper) other).person.getName().equals(person.getName());
        } else {
            return false;
        }
    }
    public int hashCode() {
        return person.getName().hashCode();
    }
}

Таким образом, вы сможете эффективно фильтровать уникальные объекты Person по их именам.

0

Можно упростить задачу, используя TreeSet с пользовательским компаратором. Вот пример кода:

persons.stream()
    .collect(Collectors.toCollection(
      () -> new TreeSet<Person>((p1, p2) -> p1.getName().compareTo(p2.getName()))
));

В этом примере мы создаем TreeSet, который автоматически сортирует объекты класса Person по имени. Это позволяет легко собрать элементы в упорядоченный набор, избегая дублирования и сохраняя уникальность.

0

Вы можете использовать коллектор groupingBy следующим образом:

persons.collect(Collectors.groupingBy(p -> p.getName()))
       .values()
       .forEach(t -> System.out.println(t.get(0).getId()));

Если вам нужно создать другой стрим, вы можете использовать следующий код:

persons.collect(Collectors.groupingBy(p -> p.getName()))
       .values()
       .stream()
       .map(l -> l.get(0));

Этот подход позволяет сгруппировать объекты по их именам и затем, в зависимости от ваших нужд, извлекать из групп нужные данные.

0

Вы можете использовать более современный стиль Java 8 для группировки и извлечения первых элементов из каждой группы. Вот как это можно сделать, используя Collectors.toMap вместе с Stream API:

List<Person> result = persons.stream()
    .collect(Collectors.toMap(
        Person::getName,       // Группируем по имени
        Function.identity(),   // Значение - сам объект Person
        (existing, replacement) -> existing  // В случае дубликатов, оставляем первый
    ))
    .values()
    .stream()
    .collect(Collectors.toList());

Такой подход более читабелен и использует возможности Java 8 для работы со стримами и коллекциями. Надеюсь, это поможет!

Чтобы ответить на вопрос, пожалуйста, войдите или зарегистрируйтесь