在java中获得两个集合之间的对称差的最佳方法是什么?

问题描述 投票:0回答:8

我想知道是否有一种快速/干净的方法来获得两组之间的对称差异?

我有:

Set<String> s1 = new HashSet<String>();
s1.add("a");
s1.add("b");
s1.add("c");

Set<String> s2 = new HashSet<String>();
s2.add("b");

我需要类似的东西:

Set<String> diff = Something.diff(s1, s2);
// diff would contain ["a", "c"]

只是为了澄清我需要对称差异。

java collections set
8个回答
53
投票

您可以使用 Google Guava 库中的一些功能(这真的很棒,我强烈推荐它!):

Sets.difference(s1, s2);
Sets.symmetricDifference(s1, s2);

difference()symmetryDifference()

的 Javadocs

symmetricDifference()
完全符合 您所要求的,但
difference()
也常常很有帮助。

这两种方法都会返回实时视图,但您可以例如在结果集上调用

.immutableCopy()
来获取不变的集。如果您不需要视图,但需要可以修改的集合实例,请调用
.copyInto(s3)
。请参阅 SetView 了解这些方法。


36
投票

您想要对称差异

public static <T> Set<T> diff(final Set<? extends T> s1, final Set<? extends T> s2) {
    Set<T> symmetricDiff = new HashSet<T>(s1);
    symmetricDiff.addAll(s2);
    Set<T> tmp = new HashSet<T>(s1);
    tmp.retainAll(s2);
    symmetricDiff.removeAll(tmp);
    return symmetricDiff;
}

如果您想要一个库,Apache Commons CollectionUtils

CollectionUtils.disjunction(s1, s2)

返回一个非泛型

Collection

番石榴套装

Sets.symmetricDifference(s1, s2)

它返回一个不可修改的

Set
作为通用
Sets.SetView

Guava 更现代一点,支持泛型,但其中任何一个都可以。


10
投票

如果您可以使用 Apache-Commons Collections,那么您正在寻找

CollectionUtils.disjunction(Collection a, Collection b)
。它返回两个集合的对称差异。

如果不是,将两个集合的交集 (

removeAll
) 减去 (
retainAll
) 来得到两个集合 (
addAll
) 的并集:

Set<String> intersection = new HashSet<String>(set1);
intersection.retainAll(set2);

Set<String> difference = new HashSet<String>();
difference.addAll(set1);
difference.addAll(set2);
difference.removeAll(intersection);

4
投票

循环一组并进行比较。

只需

O(n)
循环浏览其中一组即可。 考虑这段代码:

for (String key: oldSet) {
    if (newSet.contains(key))
        newSet.remove(key);
    else
        newSet.add(key);
}

并且

newSet
现在将仅包含两个集合中的唯一条目。 它很快,因为您只需要循环遍历其中一个集合中的元素,并且不必创建集合,除非您明确需要一个副本。


2
投票
public class Practice {
    public static void main(String[] args) {
        Set<Integer> set1 = new HashSet<Integer>();
        Set<Integer> set2 = new HashSet<Integer>();
        set1.add(1);
        set1.add(4);
        set1.add(7);
        set1.add(9);

        set2.add(2);
        set2.add(4);
        set2.add(5);
        set2.add(6);
        set2.add(7);

        symmetricSetDifference(set1, set2);
    }

    public static void symmetricSetDifference(Set<Integer>set1, Set<Integer>set2){
        //creating a new set
        Set<Integer> newSet = new HashSet<Integer>(set1);
        newSet.removeAll(set2);
        set2.removeAll(set1);
        newSet.addAll(set2);
        System.out.println(newSet);
    }

}


2
投票

Java 8 解决方案

我们可以在某个类中编写两个实用方法(适用于 java 8 及更早版本)

SetUtils (say)

public static <T> Set<T> symmetricDifferenceJava8(final Set<T> setOne, final Set<T> setTwo) {
    Set<T> result = new HashSet<>(setOne);
    setTwo.stream().filter(not(resultSet::add)).forEach(resultSet::remove);
    return result;
}

public static <T> Set<T> symmetricDifference(final Set<T> setOne, final Set<T> setTwo) {
    Set<T> result = new HashSet<T>(setOne);
    for (T element : setTwo) {
        if (!result.add(element)) {
            result.remove(element);
        }
    }
    return result;
}

public static <T> Predicate<T> not(Predicate<T> t) {
    return t.negate();
}

如果元素已经存在,则方法

add
返回 false,并且方法 negate 用于否定谓词。

Java 11

我们在 Java 11 中有一个用于谓词的 Predicate#not 方法,可以将其用作:

public static <T> Set<T> symmetricDifferenceJava11(final Set<T> setOne, final Set<T> setTwo) {
    Set<T> resultSet = new HashSet<>(setOne);
    setTwo.stream().filter(Predicate.not(resultSet::add)).forEach(resultSet::remove);
    return result;
}

1
投票
public class Practice {
    public static void main(String[] args) {
        Set<Integer> set1 = new HashSet<Integer>();
        Set<Integer> set2 = new HashSet<Integer>();
        set1.add(1);
        set1.add(4);
        set1.add(7);
        set1.add(9);

        set2.add(2);
        set2.add(4);
        set2.add(5);
        set2.add(6);
        set2.add(7);

        symmetricSetDifference(set1, set2);
    }

    public static void symmetricSetDifference(Set<Integer>set1, Set<Integer>set2){
        //creating a new set
        Set<Integer> newSet = new HashSet<Integer>(set1);
        newSet.removeAll(set2);
        set2.removeAll(set1);
        newSet.addAll(set2);
        System.out.println(newSet);
    }

如果

a
b
是集合

a - b

a
中不属于
b
的所有内容。

>>> a = {1,2,3}
>>> b = {1,4,5}
>>> 
>>> a - b
{2, 3}
>>> b - a
{4, 5}

a.symmetric_difference(b)
是一组中的所有元素,例如
a - b
b - a
的并集。

>>> a.symmetric_difference(b)
{2, 3, 4, 5}
>>> (a - b).union(b - a)
{2, 3, 4, 5}

1
投票
public static <T> Set<T> symmetricDifference(Set<? extends T> a, Set<? extends T> b) {
    return Stream.of(a, b).flatMap(Collection::stream).filter(d -> !(a.contains(d) && b.contains(d)))
            .collect(Collectors.toSet());
}
© www.soinside.com 2019 - 2024. All rights reserved.