wake-up-neo.net

Was ist der beste Weg, um den symmetrischen Unterschied zwischen zwei Sätzen in Java zu erhalten?

Ich frage mich, ob es einen schnellen/sauberen Weg gibt, den symmetrischen Unterschied zwischen zwei Sätzen zu ermitteln?

Ich habe:

Set<String> s1 = new HashSet<String>();
s1.add("a");
s1.add("b");
s1.add("c");

Set<String> s2 = new HashSet<String>();
s2.add("b");

Ich brauche etwas wie:

Set<String> diff = Something.diff(s1, s2);
// diff would contain ["a", "c"]

Nur zur Klarstellung brauche ich den symmetrischen Unterschied.

32
Simeon

Sie können einige Funktionen aus der Google Guava - Bibliothek verwenden (was wirklich toll ist, ich empfehle es dringend!):

Sets.difference(s1, s2);
Sets.symmetricDifference(s1, s2);

Javadocs für difference () und symmetricDifference ()

symmetricDifference() macht genau wonach Sie fragen , aber difference() ist oft hilfreich.

Beide Methoden geben eine Live-Ansicht zurück, aber Sie können beispielsweise .immutableCopy() in der Ergebnismenge aufrufen, um eine nicht veränderliche Menge zu erhalten. Wenn Sie keine Ansicht möchten, aber eine festgelegte Instanz benötigen, die Sie ändern können, rufen Sie .copyInto(s3) auf. Siehe SetView für diese Methoden.

40
Philipp Wendler

Sie wollen die symmetrische Differenz .

public static <T> Set<T> diff(final Set<? extends T> s1, final Set<? extends T> s2) {
    Set<T> symmetricDiff = new HashSet<T>(s1);
    symmetricDiff.addAll(s2);
    Set<T> tmp = new HashSet<T>(s1);
    tmp.retainAll(s2);
    symmetricDiff.removeAll(tmp);
    return symmetricDiff;
}

Wenn Sie eine Bibliothek benötigen, hat Apache Commons CollectionUtils

CollectionUtils.disjunction(s1, s2)

was eine nicht generische Collection zurückgibt.

und Guava Sets hat 

Sets.symmetricDifference(s1, s2)

was eine unveränderbare Set als generischen Sets.SetView zurückgibt.

Guava ist etwas moderner und unterstützt Generika, aber beide funktionieren.

31
Don Roby

Wenn Sie Apache-Commons Collections verwenden können, suchen Sie nach CollectionUtils.disjunction(Collection a, Collection b) . Es gibt die symmetrische Differenz beider Sammlungen zurück.

Wenn nicht, subtrahieren (removeAll) die Schnittmenge (retainAll) beider Mengen zur Vereinigung beider (addAll): 

Set<String> intersection = new HashSet<String>(set1);
intersection.retainAll(set2);

Set<String> difference = new HashSet<String>();
difference.addAll(set1);
difference.addAll(set2);
difference.removeAll(intersection);
4
Xavi López

Einen Satz durchlaufen und vergleichen.

Es ist nur O(n), einen der Sets zu durchlaufen. Betrachten Sie diesen Code:

for (String key: oldSet) {
    if (newSet.contains(key))
        newSet.remove(key);
    else
        newSet.add(key);
}

Die newSet enthält jetzt nur die eindeutigen Einträge aus beiden Sätzen. Das geht schnell, weil Sie nur die Elemente in einem der Sets durchlaufen müssen und keine Sets erstellen müssen, es sei denn, Sie benötigen explizit eine Kopie.

4
Erick Robertson

Java 8-Lösung

Wir können zwei Dienstprogrammmethoden (für Java 8 und frühere) in einer Klasse SetUtils (say) schreiben als:

public static <T> Set<T> symmetricDifferenceJava8(final Set<T> setOne, final Set<T> setTwo) {
    Set<T> result = new HashSet<>(setOne);
    setTwo.stream().filter(not(resultSet::add)).forEach(resultSet::remove);
    return result;
}

public static <T> Set<T> symmetricDifference(final Set<T> setOne, final Set<T> setTwo) {
    Set<T> result = new HashSet<T>(setOne);
    for (T element : setTwo) {
        if (!result.add(element)) {
            result.remove(element);
        }
    }
    return result;
}

public static <T> Predicate<T> not(Predicate<T> t) {
    return t.negate();
}

Die Methode add gibt false zurück, wenn das Element bereits vorhanden ist, und die Methode negate wird zum Negieren des Prädikats verwendet. 

Java 11

Wir haben eine Predicate # not -Methode für das Prädikat in Java 11 und können es als verwenden:

public static <T> Set<T> symmetricDifferenceJava11(final Set<T> setOne, final Set<T> setTwo) {
    Set<T> result = new HashSet<>(setOne);
    setTwo.stream().filter(Predicate.not(resultSet::add)).forEach(resultSet::remove);
    return result;
}
0
i_am_zero
public class Practice {
    public static void main(String[] args) {
        Set<Integer> set1 = new HashSet<Integer>();
        Set<Integer> set2 = new HashSet<Integer>();
        set1.add(1);
        set1.add(4);
        set1.add(7);
        set1.add(9);

        set2.add(2);
        set2.add(4);
        set2.add(5);
        set2.add(6);
        set2.add(7);

        symmetricSetDifference(set1, set2);
    }

    public static void symmetricSetDifference(Set<Integer>set1, Set<Integer>set2){
        //creating a new set
        Set<Integer> newSet = new HashSet<Integer>(set1);
        newSet.removeAll(set2);
        set2.removeAll(set1);
        newSet.addAll(set2);
        System.out.println(newSet);
    }

}

0
Suraj Upreti