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);
    }
}
FooBar
quelle

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 .

Dacwe
quelle
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());
ytterrr
quelle
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)
proaktiv-e
quelle
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;
}
Damian Leszczyński - Vash
quelle
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();
Leonardo Bernardes
quelle
4

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

Tobiasbayer
quelle
3
 newStringSet.addAll(oldStringSet);

Dies wird eine Union von s1 und s2 erzeugen

Kushan
quelle
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)
Sumit Singh
quelle
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.

Ricola
quelle
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);
Vinit Solanki
quelle
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)
UmNyobe
quelle