J'ai une classe Java Parent
avec 20 attributs (attrib1, attrib2 .. attrib20)
et ses getters et setters correspondants. De plus, j'ai deux listes d'objets Parent
: list1
et list2
.
Maintenant, je veux fusionner les deux listes et éviter les objets en double basés sur attrib1
et attrib2
.
Utilisation de Java 8:
List<Parent> result = Stream.concat(list1.stream(), list2.stream())
.distinct()
.collect(Collectors.toList());
Mais à quel endroit je dois spécifier les attributs? Devrais-je remplacer les méthodes hashCode
et equals
?
Si vous souhaitez implémenter equals
et hashCode
, vous devez le faire dans inside the class Parent
. Dans cette classe, ajoutez les méthodes comme
@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());
}
Si vous avez fait cela, distinct()
invoqué sur un Stream<Parent>
fera automatiquement le bon choix.
Si vous ne voulez pas (ou ne pouvez pas) changer la classe Parent
, il n’existe aucun mécanisme de délégation pour l’égalité, mais vous pouvez recourir à ordering car il existe un mécanisme de délégation:
Comparator<Parent> c=Comparator.comparing(Parent::getAttrib1)
.thenComparing(Parent::getAttrib2)
.thenComparing(Parent::getAttrib3)
// …
.thenComparing(Parent::getAttrib19)
.thenComparing(Parent::getAttrib20);
Ceci définit un ordre basé sur les propriétés. Cela nécessite que les types des attributs eux-mêmes soient comparables. Si vous avez une telle définition, vous pouvez l'utiliser pour implémenter l'équivalent d'une distinct()
, basée sur cette Comparator
:
List<Parent> result = Stream.concat(list1.stream(), list2.stream())
.filter(new TreeSet<>(c)::add)
.collect(Collectors.toList());
Il existe également une variante thread-safe, au cas où vous souhaiteriez l’utiliser avec des flux parallèles:
List<Parent> result = Stream.concat(list1.stream(), list2.stream())
.filter(new ConcurrentSkipListSet<>(c)::add)
.collect(Collectors.toList());
Par exemple:
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);
}
}
Remplacez les méthodes equals
et hashCode
dans la classe Parent
pour éviter les doublons dans les listes. Cela vous donnera le résultat exact que vous voulez.
Si vous souhaitez remplacer les fonctions .equals(…)
et .hashCode()
, vous devez le faire dans la classe Parent
. Notez que cela peut entraîner l'échec d'autres utilisations de Parent
. La solution liée d'Alexis C. est plus conservatrice.