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.