Отримання різниці між двома множинами


161

Тож якщо у мене є два набори:

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

Чи є спосіб порівняти їх і повернути лише набір із 4 та 5?


Можливий дублікат stackoverflow.com/questions/8064570/…
Sachin Thapa

11
Це не точний дублікат: симетрична різниця та різниця неоднакові.
Саймон Нікерсон

Якщо test1містяться 6, чи буде відповідь 4,5,6? тобто ви хочете симетричної різниці en.wikipedia.org/wiki/Symmetric_difference
Колін Д

1
якщо тест1 містив 6, я би хотів, щоб відповідь все-таки була 4, 5.
Девід Туннелл

Відповіді:


197

Спробуйте це

test2.removeAll(test1);

Встановити # removeAll

Вилучає з цього набору всі його елементи, які містяться у вказаній колекції (необов'язкова операція). Якщо вказана колекція також є множиною, ця операція ефективно змінює цей набір таким чином, що його значення є асиметричною різницею множини двох наборів.


43
Це спрацює, але я думаю, що було б непоганою особливістю встановити такі операції, як об'єднання, різниця вбудована в Java. Вищеописане рішення модифікує набір, у багатьох ситуаціях ми насправді цього не хочемо.
Praveen Kumar

129
Яким чином у Java може бути названа ця структура даних a, Setколи вона не визначається union, intersectionабо difference!!!
Джеймс Ньюмен

10
Це рішення не зовсім коректне. Тому що порядок тестування1 та тест2 має значення.
Боян Петкович

1
Повертав би test1.removeAll(test2);той самий результат, що і test2.removeAll(test1);?
дат

3
@datv Результат був би іншим. test1.removeAll(test2)- порожній набір. test2.removeAll(test1)є {4, 5}.
silentwf

122

Якщо ви використовуєте бібліотеку Guava (колишні колекції Google), є рішення:

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

Повернене SetView- це Set, це живе представлення, яке ви можете зробити незмінним або скопіювати на інший набір. test1і test2залишаються недоторканими.


6
Зауважте, що порядок тестів2 та тесту1 має значення. Існує також симетричнаDifference (), де порядок не має значення.
дат

1
symmetricDifference()принесе все, крім перехрестя, це не те, про що було задано оригінальне запитання.
Алленаз

16

Так:

test2.removeAll(test1)

Хоча це буде мутувати test2, тому створіть копію, якщо вам потрібно зберегти її.

Також ви, мабуть, мали на увазі <Integer>замість цього <int>.


7

Java 8

Ми можемо скористатися функцією removeIf, яка має предикат для написання утилітного методу як:

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

І якщо ми все ще знаходимось у попередній версії, то ми можемо використовувати removeAll як:

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

Якщо ви використовуєте Java 8, ви можете спробувати щось подібне:

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: Можливо, ви не зрозуміли, що інші відповіді не перевіряють, чи Setє більший розмір ... Тому, якщо ви намагаєтесь відняти aa менший Setвід більшого Set, ви отримаєте різні результати.
Джош М

40
ви припускаєте, що споживач цієї функції завжди хоче відняти менший набір. Різниця між наборами є антикомутативною ( en.wikipedia.org/wiki/Anticommutactivity ). AB! = BA
Саймон

7
Незалежно від того, який варіант різниці ви реалізуєте, я б використовував public static <T> Set<T> difference(final Set<T> set1, final Set<T> set2) {як підпис, метод може бути використаний як загальна функція корисності.
кап

1
@kap, але потім додайте, Comparator<T>щоб мати можливість налаштувати порівняння, оскільки equalsцього не завжди достатньо.
gervais.b

6
Це призведе до несподіваних результатів, оскільки порядок дії різниці може бути переключений, не усвідомлюючи користувача. Віднімання більшої множини з меншої множини математично чітко визначено, і для цього є безліч випадків використання.
Джоел Корнетт

3

Ви можете використовувати CollectionUtils.disjunctionдля отримання всіх відмінностей або CollectionUtils.subtractдля отримання різниці в першій колекції.

Ось приклад того, як це зробити:

    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
З якого проекту береться CollectionUtils? Чи потрібно вважати, що це з колекції Apache Commons?
Buhake Sindi

0

Просто наведіть тут один приклад (система є existingState, і ми хочемо знайти елементи для видалення (елементи, які не newStateзнаходяться, але є у них existingState) та елементи для додавання (елементи, які є, newStateале їх немає 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);

  }
}

виведе це в результаті:

Elements to add : [0, 99, 11]
Elements to remove : [1, 4]
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.