Faire la différence entre deux ensembles

161

Donc, si j'ai deux ensembles:

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

Existe-t-il un moyen de les comparer et de ne renvoyer qu'un ensemble de 4 et 5?

David Tunnell
la source
Copie possible de stackoverflow.com/questions/8064570/…
Sachin Thapa
11
Ce n'est pas un doublon exact: la différence symétrique et la différence ne sont pas les mêmes.
Simon Nickerson
Si test1contenue 6, la réponse serait 4,5,6? ie voulez-vous la différence symétrique en.wikipedia.org/wiki/Symmetric_difference
Colin D
1
si test1 contenait 6, je voudrais que la réponse soit toujours 4, 5.
David Tunnell

Réponses:

197

Essaye ça

test2.removeAll(test1);

Définir # removeAll

Supprime de cet ensemble tous ses éléments contenus dans la collection spécifiée (opération facultative). Si la collection spécifiée est également un ensemble, cette opération modifie effectivement cet ensemble afin que sa valeur soit la différence d'ensemble asymétrique des deux ensembles.

Prabhaker A
la source
43
Cela fonctionnera mais je pense que ce serait une fonctionnalité intéressante d'avoir les opérations définies comme union, difference construites en java. La solution ci-dessus modifiera l'ensemble, dans de nombreuses situations, nous ne voulons pas vraiment cela.
Praveen Kumar
130
Comment Java peut-il avoir le culot d'appeler cette structure de données une Setquand elle ne définit pas union, intersectionou difference!!!
James Newman
10
Cette solution n'est pas tout à fait correcte. Parce que l'ordre de test1 et test2 fait une différence.
Bojan Petkovic
1
Renvoyerait test1.removeAll(test2);le même résultat que test2.removeAll(test1);?
datv
3
@datv Le résultat serait différent. test1.removeAll(test2)est un ensemble vide. test2.removeAll(test1)est {4, 5}.
silentwf
122

Si vous utilisez la bibliothèque Guava (ancienne Google Collections), il existe une solution:

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

Le rendu SetViewest a Set, c'est une représentation en direct que vous pouvez soit rendre immuable, soit copier dans un autre ensemble. test1et test2sont laissés intacts.

Mikhail Golubtsov
la source
6
Notez que l' ordre de test2 et test1 est important. Il y a aussi symetricDifference () où l'ordre n'a pas d'importance.
datv
1
symmetricDifference()apportera tout sauf l'intersection, ce n'est pas ce que la question initiale demandait.
Allenaz
16

Oui:

test2.removeAll(test1)

Bien que cela mute test2, créez une copie si vous devez la conserver.

En outre, vous vouliez probablement dire <Integer>au lieu de <int>.

arshajii
la source
7

Java 8

Nous pouvons utiliser removeIf qui prend un prédicat pour écrire une méthode utilitaire comme:

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

Et au cas où nous en serions encore à une version antérieure, nous pouvons utiliser removeAll comme:

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;
}
akhil_mittal
la source
3

Si vous utilisez Java 8, vous pouvez essayer quelque chose comme ceci:

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());
}
Josh M
la source
4
@Downvoter: Vous n'avez peut-être pas réalisé que les autres réponses ne vérifient pas laquelle Setest la plus grande ... Par conséquent, si vous essayez de soustraire aa plus petit Setd'un plus grand Set, vous obtiendrez des résultats différents.
Josh M
40
vous supposez que le consommateur de cette fonction veut toujours soustraire le plus petit ensemble. La différence de réglage est anticommutative ( en.wikipedia.org/wiki/Anticommutativity ). AB! = BA
Simon
7
Quelle que soit la variante de différence que vous implémentez, j'utiliserais public static <T> Set<T> difference(final Set<T> set1, final Set<T> set2) {comme signature, la méthode est alors utilisable comme fonction d'utilité générique.
kap
1
@kap mais ajoutez ensuite un Comparator<T>pour pouvoir personnaliser la comparaison car ce equalsn'est pas toujours suffisant.
gervais.b
6
Cela conduira à des résultats inattendus car l'ordre de l'opération de différence peut être changé sans que l'utilisateur en soit conscient. La soustraction d'un ensemble plus grand d'un ensemble plus petit est mathématiquement bien définie et il existe de nombreux cas d'utilisation.
Joel Cornett
3

Vous pouvez utiliser CollectionUtils.disjunctionpour obtenir toutes les différences ou CollectionUtils.subtractpour obtenir la différence dans la première collection.

Voici un exemple de comment procéder:

    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), " , "));
pwipo
la source
3
De quel projet CollectionUtilsvient-il? Dois-je supposer qu'il provient d'Apache Commons Collection?
Buhake Sindi
0

Juste pour mettre un exemple ici (le système est dedans existingState, et nous voulons trouver des éléments à supprimer (éléments qui ne sont pas dedans newStatemais qui sont présents dans existingState) et des éléments à ajouter (éléments qui sont dedans newStatemais ne sont pas présents dans 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);

  }
}

produirait ceci comme résultat:

Elements to add : [0, 99, 11]
Elements to remove : [1, 4]
Bojan Vukasovic
la source