Manu Joy
Manu Joy

Reputation: 1769

Merging two List of objects in java 8

I have a Java class Parent with 20 attributes (attrib1, attrib2 .. attrib20) and its corresponding getters and setters. Also I have two lists of Parent objects: list1 and list2.

Now I want to merge both lists and avoid duplicate objects based on attrib1 and attrib2.

Using Java 8:

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

But in which place I have to specify the attributes? Should I override hashCode and equals method?

Upvotes: 34

Views: 63045

Answers (4)

Dominic D&#39;Souza
Dominic D&#39;Souza

Reputation: 981

Override the equals and hashCode methods in Parent class to avoid duplicates from the lists. This will give you the exact result what you want.

Upvotes: 1

Holger
Holger

Reputation: 298123

If you want to implement equals and hashCode, the place to do it is inside the class Parent. Within that class add the methods like

    @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());
    }

If you did this, distinct() invoked on a Stream<Parent> will automatically do the right thing.


If you don’t want (or can’t) change the class Parent, there is no delegation mechanism for equality, but you may resort to ordering as that has a delegation mechanism:

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

This defines an order based on the properties. It requires that the types of the attributes itself are comparable. If you have such a definition, you can use it to implement the equivalent of a distinct(), based on that Comparator:

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

There is also a thread-safe variant, in case you want to use it with parallel streams:

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

Upvotes: 32

user4910279
user4910279

Reputation:

For example:

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);
    }
}

Upvotes: 1

llogiq
llogiq

Reputation: 14511

If you want to override .equals(…) and .hashCode(), you need to do so on the Parent class. Note that this may cause other uses of Parent to fail. Alexis C.'s linked solution is more conservative.

Upvotes: 0

Related Questions