Uzyskanie różnicy między dwoma zestawami


161

Więc jeśli mam dwa zestawy:

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

Czy jest sposób na ich porównanie i zwrócono tylko zestaw 4 i 5?



11
To nie jest dokładny duplikat: różnica symetryczna i różnica to nie to samo.
Simon Nickerson,

Gdyby była test1zawarta 6, czy odpowiedź byłaby 4,5,6? czyli czy chcesz symetrycznej różnicy en.wikipedia.org/wiki/Symmetric_difference
Colin D

1
gdyby test1 zawierał 6, chciałbym, aby odpowiedź nadal wynosiła 4, 5.
David Tunnell

Odpowiedzi:


197

Spróbuj tego

test2.removeAll(test1);

Ustaw # removeAll

Usuwa z tego zestawu wszystkie jego elementy, które są zawarte w określonej kolekcji (operacja opcjonalna). Jeśli określona kolekcja jest również zbiorem, ta operacja skutecznie modyfikuje ten zestaw, tak aby jego wartość była asymetryczną różnicą między dwoma zestawami.


43
To zadziała, ale myślę, że fajnie byłoby mieć ustawione operacje, takie jak unia, różnica wbudowana w java. Powyższe rozwiązanie zmodyfikuje zestaw, w wielu sytuacjach tak naprawdę tego nie chcemy.
Praveen Kumar

129
Jak Java może mieć czelność nazywać tę strukturę danych a, Setkiedy nie definiuje union, intersectionlub difference!!!
James Newman

10
To rozwiązanie nie jest w pełni poprawne. Ponieważ kolejność test1 i test2 ma znaczenie.
Bojan Petkovic

1
Czy test1.removeAll(test2);zwróci ten sam wynik co test2.removeAll(test1);?
datv

3
@datv Wynik byłby inny. test1.removeAll(test2)jest pustym zestawem. test2.removeAll(test1)jest {4, 5}.
silentwf

122

Jeśli korzystasz z biblioteki Guava (dawne Kolekcje Google), jest rozwiązanie:

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

Zwracany SetViewjest a Set, jest to reprezentacja na żywo, którą można uczynić niezmienną lub skopiować do innego zestawu. test1i test2pozostają nienaruszone.


6
Zauważ, że kolejność testów test2 i test1 ma znaczenie. Istnieje również symmetricDifference (), gdzie kolejność nie ma znaczenia.
datv

1
symmetricDifference()przyniesie wszystko oprócz skrzyżowania, nie o to chodziło w pierwotnym pytaniu.
Allenaz

16

Tak:

test2.removeAll(test1)

Chociaż spowoduje to mutację test2, więc utwórz kopię, jeśli chcesz ją zachować.

Prawdopodobnie miałeś na myśli <Integer>zamiast <int>.


7

Java 8

Możemy skorzystać z removeIf, która przyjmuje predykat do napisania metody narzędziowej jako:

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

A jeśli nadal jesteśmy na jakiejś wcześniejszej wersji, możemy użyć removeAll jako:

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

Jeśli używasz Java 8, możesz spróbować czegoś takiego:

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
@Downvoter: Być może nie zdałeś sobie sprawy, że inne odpowiedzi nie sprawdzają, która Setjest większa ... Dlatego jeśli spróbujesz odjąć a mniejszy Setod większego Set, otrzymasz inne wyniki.
Josh M,

40
zakładasz, że odbiorca tej funkcji zawsze chce odjąć mniejszy zbiór. Różnica zestawu jest antykomutacyjna ( en.wikipedia.org/wiki/Anticommutativity ). AB! = BA
Simon

7
Bez względu na to, który wariant różnicy zaimplementujesz, użyłbym public static <T> Set<T> difference(final Set<T> set1, final Set<T> set2) {jako sygnatury, metoda jest wtedy użyteczna jako ogólna funkcja narzędzia.
kap

1
@kap, ale następnie dodaj a, Comparator<T>aby móc dostosować porównanie, ponieważ equalsnie zawsze jest wystarczające.
gervais.b

6
Doprowadzi to do nieoczekiwanych rezultatów, ponieważ kolejność operacji różnicowych może zostać zmieniona bez wiedzy użytkownika. Odejmowanie większego zbioru od mniejszego jest dobrze zdefiniowane matematycznie i istnieje wiele jego zastosowań.
Joel Cornett

3

Możesz użyć, CollectionUtils.disjunctionaby uzyskać wszystkie różnice lub CollectionUtils.subtractuzyskać różnicę w pierwszej kolekcji.

Oto przykład, jak to zrobić:

    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
Z jakiego projektu CollectionUtilspochodzi? Czy muszę zakładać, że pochodzi z Apache Commons Collection?
Buhake Sindi

0

Wystarczy podać jeden przykład (system jest w existingState, a my chcemy znaleźć elementy do usunięcia (elementy, których nie ma, newStateale w których są obecne existingState) i elementy do dodania (elementy, które są w newStateśrodku, ale nie są obecne 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);

  }
}

wyświetli to jako wynik:

Elements to add : [0, 99, 11]
Elements to remove : [1, 4]
Korzystając z naszej strony potwierdzasz, że przeczytałeś(-aś) i rozumiesz nasze zasady używania plików cookie i zasady ochrony prywatności.
Licensed under cc by-sa 3.0 with attribution required.