我想知道是否有一种快速/干净的方法来获得两组之间的对称差异?
我有:
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"]
只是为了澄清我需要对称差异。
您可以使用 Google Guava 库中的一些功能(这真的很棒,我强烈推荐它!):
Sets.difference(s1, s2);
Sets.symmetricDifference(s1, s2);
difference() 和 symmetryDifference()
的 JavadocssymmetricDifference()
完全符合 您所要求的,但 difference()
也常常很有帮助。
这两种方法都会返回实时视图,但您可以例如在结果集上调用
.immutableCopy()
来获取不变的集。如果您不需要视图,但需要可以修改的集合实例,请调用 .copyInto(s3)
。请参阅 SetView 了解这些方法。
您想要对称差异。
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 更现代一点,支持泛型,但其中任何一个都可以。
如果您可以使用 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);
循环一组并进行比较。
只需
O(n)
循环浏览其中一组即可。 考虑这段代码:
for (String key: oldSet) {
if (newSet.contains(key))
newSet.remove(key);
else
newSet.add(key);
}
并且
newSet
现在将仅包含两个集合中的唯一条目。 它很快,因为您只需要循环遍历其中一个集合中的元素,并且不必创建集合,除非您明确需要一个副本。
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);
}
}
我们可以在某个类中编写两个实用方法(适用于 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 中有一个用于谓词的 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;
}
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}
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());
}