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

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ą.


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

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)

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

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

4

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



3
 newStringSet.addAll(oldStringSet);

Spowoduje to utworzenie sumy s1 i s2


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)

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.


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

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)
Korzystając z naszej strony potwierdzasz, że przeczytałeś(-aś) i rozumiesz nasze zasady używania plików cookie i zasady ochrony prywatności.
Licensed under cc by-sa 3.0 with attribution required.