Получение разницы между двумя наборами

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
если бы test1 содержал 6, я бы хотел, чтобы ответ был 4, 5.
Дэвид Туннелл

Ответы:

197

Попробуй это

test2.removeAll(test1);

Set # RemoveAll

Удаляет из этого набора все его элементы, содержащиеся в указанной коллекции (необязательная операция). Если указанная коллекция также является набором, эта операция эффективно модифицирует этот набор так, чтобы его значение было асимметричной разностью набора двух наборов.

Прабхакер А
источник
43
Это будет работать, но я думаю, что было бы неплохо иметь встроенные операции, такие как объединение, разница, встроенная в Java. Вышеупомянутое решение изменит набор, во многих ситуациях мы действительно не хотим этого.
Правин Кумар
130
Как может Java иметь право вызывать эту структуру данных, Setкогда она не определяет union, intersectionили difference!!!
Джеймс Ньюман
10
Это решение не совсем правильно. Потому что порядок test1 и test2 имеет значение.
Боян Петкович
1
Вернул бы test1.removeAll(test2);тот же результат, что и test2.removeAll(test1);?
datv
3
@datv Результат будет другим. test1.removeAll(test2)это пустой набор test2.removeAll(test1)есть {4, 5}.
silentwf
122

Если вы используете библиотеку Guava (ранее Google Collections), есть решение:

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

Возвращается SetViewa Set, это живое представление, которое вы можете сделать неизменным или скопировать в другой набор. test1и test2остались нетронутыми.

Михаил Голубцов
источник
6
Обратите внимание, что порядок test2 и test1 имеет значение. Там также есть симметричнаяDifference (), где порядок не имеет значения.
дата
1
symmetricDifference()принесет все, кроме пересечения, это не то, о чем просил оригинальный вопрос.
Allenaz
16

Да:

test2.removeAll(test1)

Хотя это будет видоизменяться test2, поэтому создайте копию, если вам нужно ее сохранить.

Кроме того, вы, вероятно, имели в виду, <Integer>а не <int>.

arshajii
источник
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;
}
akhil_mittal
источник
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них больше ... Поэтому, если вы пытаетесь вычесть меньшее Setиз большего Set, вы получите другие результаты.
Джош М
40
Вы предполагаете, что потребитель этой функции всегда хочет вычесть меньший набор. Разница в множествах антикоммутативна ( en.wikipedia.org/wiki/Anticommutativity ). AB! = BA
Simon
7
Независимо от того, какой вариант различий вы реализуете, я бы использовал его в public static <T> Set<T> difference(final Set<T> set1, final Set<T> set2) {качестве сигнатуры, тогда метод можно использовать как универсальную служебную функцию.
Кап
1
@kap, но затем добавьте a, 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), " , "));
pwipo
источник
3
Из какого проекта CollectionUtils? Нужно ли предполагать, что это из коллекции Apache Commons?
Бухаке Синди
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]
Боян Вукасович
источник