Czy jest lepszy sposób na połączenie dwóch zestawów ciągów w java?

90

Muszę połączyć dwa zestawy ciągów, odfiltrowując zbędne informacje, to jest rozwiązanie, które wymyśliłem, czy jest lepszy sposób, który każdy może zasugerować? Może coś wbudowanego, co przeoczyłem? Nie miałem szczęścia z Google.

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

for(String currentString : oldStringSet)
{
    if (!newStringSet.contains(currentString))
    {
        newStringSet.add(currentString);
    }
}
FooBar
źródło

Odpowiedzi:

116

Ponieważ a Setnie zawiera zduplikowanych wpisów, możesz więc połączyć je w następujący sposób:

newStringSet.addAll(oldStringSet);

Nie ma znaczenia, czy dodasz rzeczy dwa razy, zestaw będzie zawierał element tylko raz… np. Nie ma potrzeby sprawdzania containsmetodą.

dacwe
źródło
88

Możesz to zrobić za pomocą tej jednej linijki

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

Z importem statycznym wygląda jeszcze ładniej

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

Innym sposobem jest użycie metody flatMap :

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

Również każdą kolekcję można było łatwo połączyć z jednym elementem

Set<String> combined = concat(newStringSet.stream(), Stream.of(singleValue))
        .collect(toSet());
ytterrr
źródło
Jak to jest lepsze niż addAll?
KKlalala
7
@KKlalala, twoje wymagania określą, co jest lepsze. Główna różnica między addAlli korzystaniem ze strumieni jest następująca: • używanie set1.addAll(set2)będzie miało efekt uboczny polegający na fizycznej zmianie zawartości set1. • Jednak użycie strumieni zawsze spowoduje, że nowe wystąpienie będzie Setzawierało zawartość obu zestawów bez modyfikowania żadnej z oryginalnych instancji zestawu. IMHO ta odpowiedź jest lepsza, ponieważ pozwala uniknąć skutków ubocznych i potencjalnych nieoczekiwanych zmian w oryginalnym zestawie, gdyby miał być użyty w innym miejscu, oczekując oryginalnej zawartości. HTH
edwardsmatt
1
Ma to również tę zaletę, że obsługuje niezmienne zestawy. Zobacz: docs.oracle.com/javase/8/docs/api/java/util/…
edwardsmatt
34

To samo z guawą :

Set<String> combinedSet = Sets.union(oldStringSet, newStringSet)
proactive-e
źródło
2
Sets :: union to świetny BinaryOperator do użycia z Collectors.reducing ().
mskfisher
12

Z definicji Zestaw zawiera tylko unikalne elementy.

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

Aby ulepszyć swój kod, możesz stworzyć w tym celu ogólną metodę

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
źródło
6

Jeśli używasz guawy, możesz również użyć kreatora, aby uzyskać większą elastyczność:

ImmutableSet.<String>builder().addAll(someSet)
                              .addAll(anotherSet)
                              .add("A single string")
                              .build();
Leonardo Bernardes
źródło
4

Po prostu użyj newStringSet.addAll(oldStringSet). Nie ma potrzeby sprawdzania duplikatów, ponieważ Setimplementacja już to robi.

tobiasbayer
źródło
3
 newStringSet.addAll(oldStringSet);

Spowoduje to utworzenie sumy s1 i s2

Kushan
źródło
2

Użyj boolean addAll(Collection<? extends E> c)
Dodaje wszystkie elementy w określonej kolekcji do tego zestawu, jeśli nie są one jeszcze obecne (operacja opcjonalna). Jeśli określona kolekcja jest również zbiorem, operacja addAll skutecznie modyfikuje ten zestaw, tak aby jego wartość była sumą dwóch zestawów. Zachowanie tej operacji jest niezdefiniowane, jeśli określona kolekcja zostanie zmodyfikowana w trakcie trwania operacji.

newStringSet.addAll(oldStringSet)
Sumit Singh
źródło
2

Jeśli zależy Ci na wydajności, a nie musisz trzymać swoich dwóch zestawów, a jeden z nich może być ogromny, proponuję sprawdzić, który zestaw jest największy i dodać elementy z najmniejszego.

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;
}

W ten sposób, jeśli twój nowy zestaw ma 10 elementów, a stary zestaw 100 000, wykonujesz tylko 10 operacji zamiast 100 000.

Ricola
źródło
To bardzo dobra logika, której nie mogę sobie wyobrazić, dlaczego nie ma tego w głównym parametrze metody addAll, jakpublic boolean addAll(int index, Collection<? extends E> c, boolean checkSizes)
Gaspar
Myślę, że z powodu samej specyfikacji: Dodaje wszystkie elementy z określonej kolekcji do tej kolekcji . Mógłbyś mieć inną metodę, ale byłoby to dość mylące, gdyby nie spełniała tej samej specyfikacji, co metody, które przeciąża.
Ricola
Tak, mówiłem o innej metodzie przeciążającej tę
Gaspar
2

Jeśli używasz Apache Common, użyj SetUtilsklasy zorg.apache.commons.collections4.SetUtils;

SetUtils.union(setA, setB);
Vinit Solanki
źródło
Zwróć uwagę, że zwraca wartość a SetView, która jest niezmienna.
jaco0646
2
Set.addAll()

Dodaje wszystkie elementy w określonej kolekcji do tego zestawu, jeśli nie są jeszcze obecne (operacja opcjonalna). Jeśli określona kolekcja jest również zbiorem, operacja addAll skutecznie modyfikuje ten zestaw, tak aby jego wartość była sumą dwóch zestawów

newStringSet.addAll(oldStringSet)
UmNyobe
źródło