What is the best way get the symmetric difference between two sets in java?
I'm wondering if there is a quick/clean way to get the symmetric difference between two sets ?
I have:
Set<String> s1 = new HashSet<String>();
s1.add("a");
s1.add("b");
s1.add("c");
Set<String> s2 = new HashSet<String>();
s2.add("b");
I need something like:
Set<String> diff = Something.diff(s1, s2);
// diff would contain ["a", "c"]
Just to clarify I need the symmetric difference.
You can use some functions from the Google Guava library (which is really great, I strongly recommend it!):
Sets.difference(s1, s2);
Sets.symmetricDifference(s1, s2);
Javadocs for difference() and symmetricDifference()
symmetricDifference()
does exactly what you are asking for, but difference()
is also often helpful.
Both methods return a live view, but you can for example call .immutableCopy()
on the resulting set to get a non-changing set. If you don't want a view, but need a set instance you can modify, call .copyInto(s3)
. See SetView for these methods.
You want the symmetric difference.
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;
}
If you want a library, Apache Commons CollectionUtils has
CollectionUtils.disjunction(s1, s2)
which returns a non-generic Collection
.
and Guava Sets has
Sets.symmetricDifference(s1, s2)
which returns an unmodifiable Set
as a generic Sets.SetView
.
Guava is a bit more modern, supporting generics, but either of these will work.
If you can use Apache-Commons Collections, you are looking for CollectionUtils.disjunction(Collection a, Collection b)
. It returns the symmetric difference of both Collections.
If not, substract (removeAll
) the intersection (retainAll
) of both sets to the union of both (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);
Loop through one set and compare.
It's only O(n)
to loop through one of the sets. Consider this code:
for (String key: oldSet) {
if (newSet.contains(key))
newSet.remove(key);
else
newSet.add(key);
}
And the newSet
will now contain only the unique entries from both sets. It's fast, because you only need to loop through the elements in one of the sets and you don't have to create sets unless you explicitly need a copy.