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?



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

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.


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.


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


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

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

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

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]
Ao utilizar nosso site, você reconhece que leu e compreendeu nossa Política de Cookies e nossa Política de Privacidade.
Licensed under cc by-sa 3.0 with attribution required.