Ottenere la differenza tra due set


161

Quindi se ho due set:

Set<Integer> test1 = new HashSet<Integer>();
test1.add(1);
test1.add(2);
test1.add(3);

Set<Integer> test2 = new HashSet<Integer>();
test2.add(1);
test2.add(2);
test2.add(3);
test2.add(4);
test2.add(5);

C'è un modo per confrontarli e restituire solo un set di 4 e 5?



11
Questo non è un duplicato esatto: la differenza simmetrica e la differenza non sono le stesse.
Simon Nickerson,

Se test1contenuta 6, la risposta sarebbe 4,5,6? cioè vuoi la differenza simmetrica en.wikipedia.org/wiki/Symmetric_difference
Colin D

1
se test1 contenesse 6, vorrei che la risposta fosse ancora 4, 5.
David Tunnell,

Risposte:


197

Prova questo

test2.removeAll(test1);

Set # removeAll

Rimuove da questo set tutti i suoi elementi contenuti nella raccolta specificata (operazione opzionale). Se anche la raccolta specificata è un set, questa operazione modifica efficacemente questo set in modo che il suo valore sia la differenza del set asimmetrico dei due set.


43
Funzionerà ma penso che sarebbe una buona caratteristica avere le operazioni impostate come unione, differenza costruita in Java. La soluzione di cui sopra modificherà il set, in molte situazioni non lo vogliamo davvero.
Praveen Kumar,

130
Come può Java avere il coraggio di chiamare questa struttura di dati a Setquando non definisce union, intersectiono difference!!!
James Newman,

10
Questa soluzione non è completamente corretta. Perché l'ordine di test1 e test2 fa la differenza.
Bojan Petkovic,

1
Sarebbe test1.removeAll(test2);tornare lo stesso risultato test2.removeAll(test1);?
datv

3
@datv Il risultato sarebbe diverso. test1.removeAll(test2)è un set vuoto. test2.removeAll(test1)lo è {4, 5}.
silentwf,

122

Se usi la libreria Guava (ex Google Collections) c'è una soluzione:

SetView<Number> difference = com.google.common.collect.Sets.difference(test2, test1);

Il reso SetViewè a Set, è una rappresentazione dal vivo che puoi rendere immutabile o copiare su un altro set. test1e test2rimangono intatti.


6
Si noti che l' ordine di test2 e test1 è importante. C'è anche symmetricDifference () in cui l'ordine non ha importanza.
datv

1
symmetricDifference()porterà tutto tranne l'intersezione, non è questa la domanda originale.
Allenaz,

16

Sì:

test2.removeAll(test1)

Anche se questo cambierà test2, quindi creane una copia se devi conservarlo.

Inoltre, probabilmente intendevi <Integer>invece di <int>.


7

Java 8

Possiamo usare removeIf che richiede un predicato per scrivere un metodo di utilità come:

// computes the difference without modifying the sets
public static <T> Set<T> differenceJava8(final Set<T> setOne, final Set<T> setTwo) {
     Set<T> result = new HashSet<T>(setOne);
     result.removeIf(setTwo::contains);
     return result;
}

E nel caso in cui siamo ancora in una versione precedente, possiamo usare removeAll come:

public static <T> Set<T> difference(final Set<T> setOne, final Set<T> setTwo) {
     Set<T> result = new HashSet<T>(setOne);
     result.removeAll(setTwo);
     return result;
}

3

Se stai usando Java 8, potresti provare qualcosa del genere:

public Set<Number> difference(final Set<Number> set1, final Set<Number> set2){
    final Set<Number> larger = set1.size() > set2.size() ? set1 : set2;
    final Set<Number> smaller = larger.equals(set1) ? set2 : set1;
    return larger.stream().filter(n -> !smaller.contains(n)).collect(Collectors.toSet());
}

4
@Downvoter: Forse non hai capito che le altre risposte non controllano per vedere quale Setè più grande ... Pertanto, se stai cercando di sottrarre un più piccolo Setda uno più grande Set, otterrai risultati diversi.
Josh M,

40
stai assumendo che il consumatore di quella funzione voglia sempre sottrarre l'insieme più piccolo. Impostare la differenza è anticommutativo ( en.wikipedia.org/wiki/Anticommutativity ). AB! = BA
Simon,

7
Indipendentemente dalla variante di differenza implementata, utilizzerei public static <T> Set<T> difference(final Set<T> set1, final Set<T> set2) {come firma, il metodo è quindi utilizzabile come funzione di utilità generica.
Kap

1
@kap ma poi aggiungi a Comparator<T>per poter personalizzare il confronto perché equalsnon è sempre abbastanza.
gervais.b,

6
Ciò porterà a risultati imprevisti poiché l'ordine dell'operazione di differenza può essere commutato senza che l'utente ne sia consapevole. La sottrazione di un set più grande da un set più piccolo è matematicamente ben definita e ci sono molti casi d'uso per questo.
Joel Cornett,

3

È possibile utilizzare CollectionUtils.disjunctionper ottenere tutte le differenze oCollectionUtils.subtract per ottenere la differenza nella prima raccolta.

Ecco un esempio di come farlo:

    var collection1 = List.of(1, 2, 3, 4, 5);
    var collection2 = List.of(2, 3, 5, 6);
    System.out.println(StringUtils.join(collection1, " , "));
    System.out.println(StringUtils.join(collection2, " , "));
    System.out.println(StringUtils.join(CollectionUtils.subtract(collection1, collection2), " , "));
    System.out.println(StringUtils.join(CollectionUtils.retainAll(collection1, collection2), " , "));
    System.out.println(StringUtils.join(CollectionUtils.collate(collection1, collection2), " , "));
    System.out.println(StringUtils.join(CollectionUtils.disjunction(collection1, collection2), " , "));
    System.out.println(StringUtils.join(CollectionUtils.intersection(collection1, collection2), " , "));
    System.out.println(StringUtils.join(CollectionUtils.union(collection1, collection2), " , "));

3
Da quale progetto CollectionUtilsproviene? Devo presumere che provenga dalla collezione Apache Commons?
Buhake Sindi,

0

Solo per fare un esempio qui (il sistema è in existingStatee vogliamo trovare elementi da rimuovere (elementi che non sono in newStatema sono presenti in existingState) ed elementi da aggiungere (elementi che sono in newStatema non sono presenti existingState):

public class AddAndRemove {

  static Set<Integer> existingState = Set.of(1,2,3,4,5);
  static Set<Integer> newState = Set.of(0,5,2,11,3,99);

  public static void main(String[] args) {

    Set<Integer> add = new HashSet<>(newState);
    add.removeAll(existingState);

    System.out.println("Elements to add : " + add);

    Set<Integer> remove = new HashSet<>(existingState);
    remove.removeAll(newState);

    System.out.println("Elements to remove : " + remove);

  }
}

produrrebbe questo come risultato:

Elements to add : [0, 99, 11]
Elements to remove : [1, 4]
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.