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