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


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.