Gibt es eine bessere Möglichkeit, zwei String-Sets in Java zu kombinieren?


88

Ich muss zwei Zeichenfolgensätze kombinieren, während redundante Informationen herausgefiltert werden. Dies ist die Lösung, die ich mir ausgedacht habe. Gibt es einen besseren Weg, den jeder vorschlagen kann? Vielleicht etwas eingebaut, das ich übersehen habe? Hatte kein Glück mit Google.

Set<String> oldStringSet = getOldStringSet();
Set<String> newStringSet = getNewStringSet();

for(String currentString : oldStringSet)
{
    if (!newStringSet.contains(currentString))
    {
        newStringSet.add(currentString);
    }
}

Antworten:


116

Da a Setkeine doppelten Einträge enthält, können Sie beide kombinieren, indem Sie:

newStringSet.addAll(oldStringSet);

Es spielt keine Rolle, ob Sie Dinge zweimal hinzufügen, die Menge enthält das Element nur einmal ... z. B. ist es nicht erforderlich, die containsMethode zu überprüfen .


86

Sie können es mit diesem Einzeiler tun

Set<String> combined = Stream.concat(newStringSet.stream(), oldStringSet.stream())
        .collect(Collectors.toSet());

Mit einem statischen Import sieht es noch schöner aus

Set<String> combined = concat(newStringSet.stream(), oldStringSet.stream())
        .collect(toSet());

Eine andere Möglichkeit ist die Verwendung der flatMap- Methode:

Set<String> combined = Stream.of(newStringSet, oldStringSet).flatMap(Set::stream)
        .collect(toSet());

Auch jede Sammlung kann leicht mit einem einzigen Element kombiniert werden

Set<String> combined = concat(newStringSet.stream(), Stream.of(singleValue))
        .collect(toSet());

Wie ist das besser als addAll?
KKlalala

7
@KKlalala, Ihre Anforderungen bestimmen, welche besser ist. Der Hauptunterschied zwischen addAllund der Verwendung von Streams besteht darin, dass • die Verwendung set1.addAll(set2)den Nebeneffekt hat, dass der Inhalt von Streams physisch geändert wird set1. • Die Verwendung von Streams führt jedoch immer zu einer neuen Instanz, Setdie den Inhalt beider Sätze enthält, ohne eine der ursprünglichen Satzinstanzen zu ändern. IMHO ist diese Antwort besser, weil sie Nebenwirkungen und das Potenzial für unerwartete Änderungen am Originalset vermeidet, wenn es an anderer Stelle verwendet wird, während der ursprüngliche Inhalt erwartet wird. HTH
edwardsmatt

1
Dies hat auch den Vorteil, dass unveränderliche Sets unterstützt werden. Siehe: docs.oracle.com/javase/8/docs/api/java/util/…
edwardsmatt

33

Das gleiche gilt für Guave :

Set<String> combinedSet = Sets.union(oldStringSet, newStringSet)

2
Sets :: union ist ein großartiger BinaryOperator für Collectors.reducing ().
mskfisher

12

Aus der Definition enthält Set nur eindeutige Elemente.

Set<String> distinct = new HashSet<String>(); 
 distinct.addAll(oldStringSet);
 distinct.addAll(newStringSet);

Um Ihren Code zu verbessern, können Sie eine generische Methode dafür erstellen

public static <T> Set<T> distinct(Collection<T>... lists) {
    Set<T> distinct = new HashSet<T>();

    for(Collection<T> list : lists) {
        distinct.addAll(list);
    }
    return distinct;
}

5

Wenn Sie Guava verwenden, können Sie auch einen Builder verwenden, um mehr Flexibilität zu erhalten:

ImmutableSet.<String>builder().addAll(someSet)
                              .addAll(anotherSet)
                              .add("A single string")
                              .build();

4

Einfach benutzen newStringSet.addAll(oldStringSet). Sie müssen nicht nach Duplikaten suchen, da die SetImplementierung dies bereits tut.



3
 newStringSet.addAll(oldStringSet);

Dies wird eine Union von s1 und s2 erzeugen


2

Verwendung boolean addAll(Collection<? extends E> c)
Fügt diesem Satz alle Elemente in der angegebenen Auflistung hinzu, falls sie noch nicht vorhanden sind (optionaler Vorgang). Wenn die angegebene Auflistung auch eine Menge ist, ändert die Operation addAll diese Menge effektiv, sodass ihr Wert die Vereinigung der beiden Mengen ist. Das Verhalten dieser Operation ist undefiniert, wenn die angegebene Sammlung während der Operation geändert wird.

newStringSet.addAll(oldStringSet)

2

Wenn Sie Wert auf Leistung legen und Ihre beiden Sätze nicht behalten müssen und einer davon sehr groß sein kann, würde ich empfehlen, zu überprüfen, welcher Satz der größte ist, und die Elemente aus dem kleinsten hinzuzufügen.

Set<String> newStringSet = getNewStringSet();
Set<String> oldStringSet = getOldStringSet();

Set<String> myResult;
if(oldStringSet.size() > newStringSet.size()){
    oldStringSet.addAll(newStringSet);
    myResult = oldStringSet;
} else{
    newStringSet.addAll(oldStringSet);
    myResult = newStringSet;
}

Wenn Ihr neuer Satz 10 Elemente und Ihr alter Satz 100 000 enthält, führen Sie auf diese Weise nur 10 Operationen anstelle von 100 000 aus.


Dies ist eine sehr gute Logik, die ich mir nicht vorstellen kann, warum dies nicht im Hauptparameter der addAll-Methode steht, wiepublic boolean addAll(int index, Collection<? extends E> c, boolean checkSizes)
Gaspar

Ich denke wegen der Spezifikation selbst: Fügt alle Elemente in der angegebenen Sammlung zu dieser Sammlung hinzu . Sie könnten zwar eine andere Methode haben, aber es wäre ziemlich verwirrend, wenn sie nicht der gleichen Spezifikation folgt wie die Methoden, die sie überladen.
Ricola

Ja, ich sagte eine andere Methode, die diese überlastet
Gaspar

2

Wenn Sie Apache Common verwenden, verwenden Sie SetUtilsclass fromorg.apache.commons.collections4.SetUtils;

SetUtils.union(setA, setB);

Beachten Sie, dass dies ein SetViewunveränderliches a zurückgibt .
jaco0646

2
Set.addAll()

Fügt diesem Satz alle Elemente in der angegebenen Auflistung hinzu, falls sie noch nicht vorhanden sind (optionaler Vorgang). Wenn die angegebene Auflistung auch eine Menge ist, ändert die Operation addAll diese Menge effektiv, sodass ihr Wert die Vereinigung der beiden Mengen ist

newStringSet.addAll(oldStringSet)
Durch die Nutzung unserer Website bestätigen Sie, dass Sie unsere Cookie-Richtlinie und Datenschutzrichtlinie gelesen und verstanden haben.
Licensed under cc by-sa 3.0 with attribution required.