Obtendo a diferença entre dois conjuntos

161

Então, se eu tiver dois conjuntos:

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 uma maneira de compará-los e retornar apenas um conjunto de 4 e 5?

David Tunnell
fonte
Possível duplicata de stackoverflow.com/questions/8064570/…
Sachin Thapa 5/13
11
Esta não é uma duplicata exata: diferença simétrica e diferença não são as mesmas.
Simon Nickerson
Se test1contida 6, a resposta seria 4,5,6? ou seja, você quer a diferença simétrica pt.wikipedia.org/wiki/Symmetric_difference
Colin D
1
Se test1 continha 6, eu gostaria que a resposta ainda ser 4, 5.
David Tunnell
Java 8: stackoverflow.com/a/52268884/1216775
akhil_mittal 8/08

Respostas:

197

Tente isto

test2.removeAll(test1);

Definir # removeAll

Remove deste conjunto todos os seus elementos que estão contidos na coleção especificada (operação opcional). Se a coleção especificada também for um conjunto, essa operação modificará efetivamente esse conjunto para que seu valor seja a diferença de conjunto assimétrico dos dois conjuntos.

Prabhaker A
fonte
43
Isso funcionará, mas acho que seria um bom recurso ter as operações definidas, como union, diferença, construídas em java. A solução acima modificará o conjunto, em muitas situações nós realmente não queremos isso.
Praveen Kumar
129
Como o Java pode ter a ousadia de chamar essa estrutura de dados de Setquando não define union, intersectionou difference!!!
James Newman
10
Esta solução não está totalmente correta. Porque a ordem de test1 e test2 faz a diferença.
Bojan Petkovic
1
Iria test1.removeAll(test2);devolver o mesmo resultado que test2.removeAll(test1);?
DATV
3
@datv O resultado seria diferente. test1.removeAll(test2)é um conjunto vazio. test2.removeAll(test1)é {4, 5}.
Silencioso #
122

Se você usa a biblioteca Guava (ex-coleções do Google), existe uma solução:

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

O retorno SetViewé a Set, é uma representação ao vivo que você pode tornar imutável ou copiar para outro conjunto. test1e test2são deixados intactos.

Mikhail Golubtsov
fonte
6
Observe que a ordem de teste2 e teste1 é importante. Também há symmetricDifference () em que o pedido não importa.
DATV
1
symmetricDifference()trará tudo, menos a interseção, não é isso que a pergunta original pediu.
Allenaz
16

Sim:

test2.removeAll(test1)

Embora isso mude test2, crie uma cópia se precisar preservá-la.

Além disso, você provavelmente quis dizer em <Integer>vez de <int>.

arshajii
fonte
7

Java 8

Podemos usar removeIf, que requer um predicado para escrever um método utilitário como:

// 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 caso ainda estamos em uma versão anterior, podemos usar removeAll como:

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
fonte
3

Se você estiver usando o Java 8, pode tentar algo como isto:

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
fonte
4
@ Downowner: Talvez você não tenha percebido que as outras respostas não verificam qual Seté maior ... Portanto, se você está tentando subtrair um menor Setde um maior Set, receberá resultados diferentes.
Josh M
40
você está assumindo que o consumidor dessa função sempre deseja subtrair o conjunto menor. A diferença do conjunto é anticomutativa ( en.wikipedia.org/wiki/Anticommutativity ). AB! = BA
Simon
7
Independentemente de qual variante de diferença você implemente, eu usaria public static <T> Set<T> difference(final Set<T> set1, final Set<T> set2) {como assinatura; o método é utilizável como função de utilitário genérica.
kap
1
@ kap, mas adicione um Comparator<T>para poder personalizar a comparação, porque equalsnem sempre é suficiente.
gervais.b
6
Isso levará a resultados inesperados, pois a ordem da operação da diferença pode ser alterada sem que o usuário esteja ciente. A subtração de um conjunto maior de um conjunto menor é matematicamente bem definida e há muitos casos de uso para isso.
Joel Cornett
3

Você pode usar CollectionUtils.disjunctionpara obter todas as diferenças ou CollectionUtils.subtractobter a diferença na primeira coleção.

Aqui está um exemplo de como fazer isso:

    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
fonte
3
De qual projeto CollectionUtilsvem? Preciso assumir que é da coleção Apache Commons?
Buhake Sindi 15/02/19
0

Apenas para colocar um exemplo aqui (o sistema está dentro existingStatee queremos encontrar elementos para remover (elementos que não newStateestão mas estão presentes existingState) e elementos a serem adicionados (elementos que estão dentro newStatemas não estão presentes 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);

  }
}

produziria isso como resultado:

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