Obteniendo la diferencia entre dos conjuntos

161

Entonces, si tengo dos 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);

¿Hay alguna manera de compararlos y solo devolver un conjunto de 4 y 5?

David Tunnell
fuente
Posible duplicado de stackoverflow.com/questions/8064570/…
Sachin Thapa
11
Este no es un duplicado exacto: la diferencia simétrica y la diferencia no son lo mismo.
Simon Nickerson
Si está test1contenido 6, ¿la respuesta sería 4,5,6? es decir, ¿quieres la diferencia simétrica en.wikipedia.org/wiki/Symmetric_difference
Colin D
1
si test1 contuviera 6, me gustaría que la respuesta siguiera siendo 4, 5.
David Tunnell

Respuestas:

197

Prueba esto

test2.removeAll(test1);

Establecer # removeAll

Elimina de este conjunto todos sus elementos contenidos en la colección especificada (operación opcional). Si la colección especificada también es un conjunto, esta operación modifica efectivamente este conjunto para que su valor sea la diferencia de conjunto asimétrico de los dos conjuntos.

Prabhaker A
fuente
43
Esto funcionará, pero creo que sería una buena característica tener las operaciones de conjunto como unión, diferencia construida en Java. La solución anterior modificará el conjunto, en muchas situaciones realmente no queremos eso.
Praveen Kumar
130
¡¿Cómo puede Java tener el descaro de llamar a esta estructura de datos Setcuando no se define union, intersectiono difference!!!
James Newman
10
Esta solución no es completamente correcta. Porque el orden de test1 y test2 hace la diferencia.
Bojan Petkovic el
1
Volvería test1.removeAll(test2);el mismo resultado que test2.removeAll(test1);?
datv
3
@datv El resultado sería diferente. test1.removeAll(test2)Es un conjunto vacío. test2.removeAll(test1)es {4, 5}.
silentwf
122

Si usa la biblioteca Guava (anteriormente Google Collections), hay una solución:

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

El resultado SetViewes un Set, es una representación en vivo que puede hacer inmutable o copiar a otro conjunto. test1y test2se dejan intactos.

Mikhail Golubtsov
fuente
66
Tenga en cuenta que el orden de test2 y test1 es importante. También hay symmetricDifference () donde el orden no importa.
datv
1
symmetricDifference()traerá todo menos la intersección, eso no es lo que pidió la pregunta original.
Allenaz
16

Si:

test2.removeAll(test1)

Aunque esto va a mutar test2, cree una copia si necesita preservarla.

Además, probablemente quisiste decir en <Integer>lugar de <int>.

arshajii
fuente
7

Java 8

Podemos hacer uso de removeIf que toma un predicado para escribir un método de utilidad 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;
}

Y en caso de que todavía estemos en alguna versión 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
fuente
3

Si está utilizando Java 8, puede intentar algo como esto:

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
fuente
44
@Downvoter: Quizás no te hayas dado cuenta de que las otras respuestas no comprueban para ver cuál Setes más grande ... Por lo tanto, si estás tratando de restar aa más pequeño Setde uno más grande Set, recibirás resultados diferentes.
Josh M
40
está asumiendo que el consumidor de esa función siempre quiere restar el conjunto más pequeño. La diferencia de conjunto es anticomutativa ( en.wikipedia.org/wiki/Anticommutativity ). AB! = BA
Simon
77
Independientemente de la variante de diferencia que implemente, usaría public static <T> Set<T> difference(final Set<T> set1, final Set<T> set2) {como firma, el método se puede usar como función de utilidad genérica.
kap
1
@kap pero luego agregue un Comparator<T>para poder personalizar la comparación porque equalsno siempre es suficiente.
gervais.b
66
Esto conducirá a resultados inesperados ya que el orden de la operación de diferencia puede cambiarse sin que el usuario lo sepa. La resta de un conjunto más grande de un conjunto más pequeño está matemáticamente bien definida y hay muchos casos de uso para ello.
Joel Cornett
3

Puede usar CollectionUtils.disjunctionpara obtener todas las diferencias o CollectionUtils.subtractpara obtener la diferencia en la primera colección.

Aquí hay un ejemplo de cómo hacerlo:

    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
fuente
3
¿De qué proyecto CollectionUtilsproviene? ¿Tengo que asumir que es de la Colección Apache Commons?
Buhake Sindi
0

Solo para poner un ejemplo aquí (el sistema está en existingState, y queremos encontrar elementos para eliminar (elementos que no newStateestán pero están presentes existingState) y elementos para agregar (elementos que están dentro newStatepero no están 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);

  }
}

daría como resultado esto:

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