web-dev-qa-db-fra.com

Fusion de deux listes d'objets en Java 8

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?

25
Manu Joy

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());
21
Holger

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

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.

0
Dominic D'Souza

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.

0
llogiq