C'è un modo migliore per combinare due set di stringhe in java?


91

Ho bisogno di combinare due set di stringhe mentre filtra le informazioni ridondanti, questa è la soluzione che ho trovato, c'è un modo migliore che chiunque possa suggerire? Forse qualcosa di incorporato che ho trascurato? Non ho avuto fortuna con Google.

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

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

Risposte:


117

Poiché a Setnon contiene voci duplicate, puoi quindi combinare i due in base a:

newStringSet.addAll(oldStringSet);

Non importa se aggiungi le cose due volte, l'insieme conterrà l'elemento solo una volta ... ad es. Non è necessario controllare usando il containsmetodo.


92

Puoi farlo usando questo one-liner

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

Con un'importazione statica sembra ancora più bello

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

Un altro modo è utilizzare il metodo flatMap :

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

Inoltre qualsiasi collezione potrebbe essere facilmente combinata con un singolo elemento

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

come è meglio di addAll?
KKlalala

7
@KKlalala, le tue esigenze determineranno quale è meglio. La principale differenza tra addAlle l'utilizzo di Stream è: • l'utilizzo set1.addAll(set2)avrà l'effetto collaterale di modificare fisicamente il contenuto di set1. • Tuttavia, l'utilizzo di Streams risulterà sempre in una nuova istanza Setcontenente il contenuto di entrambi i set senza modificare nessuna delle istanze Set originali. IMHO questa risposta è migliore perché evita effetti collaterali e il potenziale per modifiche inaspettate al set originale se dovesse essere utilizzato altrove mentre si aspettano i contenuti originali. HTH
edwardsmatt

1
Questo ha anche il vantaggio di supportare i set immutabili. Vedi: docs.oracle.com/javase/8/docs/api/java/util/…
edwardsmatt

34

Lo stesso con Guava :

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

2
Sets :: union è un ottimo BinaryOperator da usare con Collectors.reducing ().
mskfisher

12

Dalla definizione Set contengono solo elementi unici.

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

Per migliorare il tuo codice puoi creare un metodo generico per questo

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

7

Se stai usando Guava puoi anche usare un builder per ottenere maggiore flessibilità:

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

4

Basta usare newStringSet.addAll(oldStringSet). Non è necessario controllare i duplicati poiché l' Setimplementazione lo fa già.



3
 newStringSet.addAll(oldStringSet);

Questo produrrà l'unione di s1 e s2


2

Usa boolean addAll(Collection<? extends E> c)
Aggiunge a questo set tutti gli elementi nella raccolta specificata se non sono già presenti (operazione facoltativa). Se la raccolta specificata è anche un set, l'operazione addAll modifica efficacemente questo set in modo che il suo valore sia l'unione dei due set. Il comportamento di questa operazione non è definito se la raccolta specificata viene modificata mentre l'operazione è in corso.

newStringSet.addAll(oldStringSet)

2

Se ti interessano le prestazioni e se non hai bisogno di mantenere i tuoi due set e uno di essi può essere enorme, ti suggerirei di controllare quale set è il più grande e aggiungere gli elementi dal più piccolo.

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

In questo modo, se il tuo nuovo set ha 10 elementi e il tuo vecchio set ne ha 100.000, fai solo 10 operazioni invece di 100.000.


Questa è una logica molto buona che non riesco a immaginare perché questo non è nel parametro principale del metodo addAll, comepublic boolean addAll(int index, Collection<? extends E> c, boolean checkSizes)
Gaspar

Immagino che a causa della specifica stessa: aggiunge tutti gli elementi nella raccolta specificata a questa raccolta . Potresti davvero avere un altro metodo, ma sarebbe abbastanza confuso se non seguisse le stesse specifiche dei metodi che sovraccarica.
Ricola

Sì, stavo dicendo un altro metodo che sovraccarica quello
Gaspare

2

Se stai usando Apache Common, usa la SetUtilsclasse daorg.apache.commons.collections4.SetUtils;

SetUtils.union(setA, setB);

Nota che questo restituisce a SetView, che è immutabile.
jaco0646

Anche ottenere size () da a SetViewè sempre un'operazione lineare.
Jugbot

2
Set.addAll()

Aggiunge tutti gli elementi nella raccolta specificata a questo set se non sono già presenti (operazione facoltativa). Se anche la raccolta specificata è un set, l'operazione addAll modifica efficacemente questo set in modo che il suo valore sia l'unione dei due set

newStringSet.addAll(oldStringSet)
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.