it-swarm.com.ru

Объединение двух списков объектов в Java 8

У меня есть класс Java Parent с 20 атрибутами (attrib1, attrib2 .. attrib20) и соответствующими ему геттерами и сеттерами. Также у меня есть два списка объектов Parent: list1 и list2.

Теперь я хочу объединить оба списка и избежать дублирования объектов на основе attrib1 и attrib2.

Используя Java 8:

List<Parent> result = Stream.concat(list1.stream(), list2.stream())
                .distinct()
                .collect(Collectors.toList());   

Но в каком месте я должен указать атрибуты? Должен ли я переопределить метод hashCode и equals?

25
Manu Joy

Если вы хотите реализовать equals и hashCode, место для этого - inside класс Parent. В этом классе добавьте такие методы, как

    @Override
    public int hashCode() {
        return Objects.hash(getAttrib1(), getAttrib2(), getAttrib3(),
            // …
                            getAttrib19(), getAttrib20());
    }

    @Override
    public boolean equals(Object obj) {
        if(this==obj) return true;
        if(!(obj instanceof Parent)) return false;
        Parent p=(Parent) obj;
        return Objects.equals(getAttrib1(), p.getAttrib1())
            && Objects.equals(getAttrib2(), p.getAttrib2())
            && Objects.equals(getAttrib3(), p.getAttrib3())
            // …
            && Objects.equals(getAttrib19(), p.getAttrib19())
            && Objects.equals(getAttrib20(), p.getAttrib20());
    }

Если вы сделали это, функция distinct(), вызванная на Stream<Parent>, автоматически сделает правильные вещи.


Если вы не хотите (или не можете) изменить класс Parent, не существует механизма делегирования для равенства, но вы можете прибегнуть к упорядочению , поскольку в нем есть механизм делегирования:

Comparator<Parent> c=Comparator.comparing(Parent::getAttrib1)
        .thenComparing(Parent::getAttrib2)
        .thenComparing(Parent::getAttrib3)
        // …
        .thenComparing(Parent::getAttrib19)
        .thenComparing(Parent::getAttrib20);

Это определяет порядок на основе свойств. Это требует, чтобы типы самих атрибутов были сопоставимы. Если у вас есть такое определение, вы можете использовать его для реализации эквивалента distinct(), основываясь на этом Comparator:

List<Parent> result = Stream.concat(list1.stream(), list2.stream())
        .filter(new TreeSet<>(c)::add)
        .collect(Collectors.toList());

Существует также потокобезопасный вариант, если вы хотите использовать его с параллельными потоками:

List<Parent> result = Stream.concat(list1.stream(), list2.stream())
        .filter(new ConcurrentSkipListSet<>(c)::add)
        .collect(Collectors.toList());
21
Holger

Например:

public class Parent {

    public int no;
    public String name;

    @Override
    public int hashCode() {
        return (no << 4) ^ name.hashCode();
    }

    @Override
    public boolean equals(Object obj) {
        if (!(obj instanceof Parent))
            return false;
        Parent o = (Parent)obj;
        return this.no == o.no && this.name.equals(o.name);
    }
}
1
saka1029

Переопределите методы equals и hashCode в классе Parent, чтобы избежать дублирования в списках. Это даст вам точный результат, что вы хотите.

0
Dominic D'Souza

Если вы хотите переопределить .equals(…) и .hashCode(), вам нужно сделать это в классе Parent. Обратите внимание, что это может привести к сбою других вариантов использования Parent. Связанное решение Alexis C. является более консервативным.

0
llogiq