What Is the Best Way Get the Symmetric Difference Between Two Sets in Java

What is the best way get the symmetric difference between two sets in java?

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.

Symmetric difference of two sets in Java

You're after the symmetric difference. This is discussed in the Java tutorial.

Set<Type> symmetricDiff = new HashSet<Type>(set1);
symmetricDiff.addAll(set2);
// symmetricDiff now contains the union
Set<Type> tmp = new HashSet<Type>(set1);
tmp.retainAll(set2);
// tmp now contains the intersection
symmetricDiff.removeAll(tmp);
// union minus intersection equals symmetric-difference

Getting the difference between two sets

Try this

test2.removeAll(test1);

Set#removeAll

Removes from this set all of its elements that are contained in the specified collection (optional operation). If the specified collection is also a set, this operation effectively modifies this set so that its value is the asymmetric set difference of the two sets.

symmetric difference of Sets in java

The symmetric difference of two sets is the union of their differences. Now, the problem in your case is that you have a method which returns a set and you modify the input sets, distorting them. You need to create a new Set and add the elements you need. In order to do that you will need to iterate your first set and add the items into the new set which are not found in the second set, and then iterate the second set and add the items to the new set which were not found in the first set. The formula is:

A △ B = (A – B) ∪ (B – A)

or:

A △ B = (A ∪ B) - (A ∩ B)

how to find difference between 2 sets without making change of any one?

You can use this :

Set<String> s3 = new HashSet<String>();
for(String temp : s1){
if(!s2.contains(temp)){
s3.add(temp);
}
}
for (String temp : s2) {
if (!s1.contains(temp)) {
s3.add(temp);
}
}

How to: Find the Set Difference Between Two Collections in Java

I have used Guava Sets.difference.

The parameters are sets and not general collections, but a handy way to create sets from any collection (with unique items) is Guava ImmutableSet.copyOf(Iterable).

How to get the symmetric difference between two Streams in Java 8?

I wasn't able to find an efficient way of doing it. The best I managed was

static <T> Stream<T> symmetricDifference(Stream<T> stream1, Stream<T> stream2) {
Set<T> elements1 = stream1.collect(Collectors.toSet());
Set<T> elements2 = stream2.collect(Collectors.toSet());
Set<T> result = new HashSet<T>(elements1);
result.addAll(elements2);
elements1.retainAll(elements2);
result.removeAll(elements1);
return result.stream();
}

which is probably the solution you already came up with.

Even when trying to use stream operations to come up with the symmetric difference, I found myself having to produce lots of temporary streams and sets in order to get over the problem that I have to iterate over the streams multiple times. Here is a version that produces a Stream containing the symmetric difference, using only stream operations. You can see that it is far more inefficient.

static <T> Stream<T> disjointStream(Stream<T> stream1, Stream<T> stream2) {
Set<T> elements1 = stream1.collect(Collectors.toSet());
Set<T> elements2 = stream2.collect(Collectors.toSet());
Set<T> elementsIn1Notin2 = elements1.stream().filter(t -> !elements2.stream().anyMatch(Predicate.isEqual(t))).collect(Collectors.toSet());
Set<T> elementsIn2Notin1 = elements2.stream().filter(t -> !elements1.stream().anyMatch(Predicate.isEqual(t))).collect(Collectors.toSet());
return Stream.concat(elementsIn1Notin2.stream(), elementsIn2Notin1.stream());
}

I'm putting this up because I'd be interested to see how it can be improved while retaining only stream operations (no Set or Collection operations apart from Collection.stream()); and also how my clunky syntax can be improved.

As an aside, for people who are just getting into Java 8 and wondering where these classes are, my imports are:

import java.util.HashSet;
import java.util.Set;
import java.util.function.Predicate;
import java.util.stream.Collectors;
import java.util.stream.Stream;

How to work with Set Differences in Java using objects?

You can achieve this with the help of overloaded subtract with predicate.

  1. Override hashcode and equals in POJO class, (and toString for clean output)

    @Override
    public boolean equals(Object o) {
    if (this == o) return true;
    if (o == null || getClass() != o.getClass()) return false;
    POJO pojo = (POJO) o;
    return Objects.equals(name, pojo.name);
    }

    @Override
    public int hashCode() {
    return name.hashCode();
    }
  2. Use Predicate with set as

    Collection<POJO> subList = CollectionUtils.subtract(p1, p2, new Predicate<POJO>() {
    @Override
    public boolean evaluate(POJO object) {
    return p1.contains(object); // might need to make p1 final
    }
    });

Result:

subList: [ Bagel ]

Note: Apache common does not support anything like Java 8 BiPredicate

Java Streams - Get a symmetric difference list from two other lists

Based on your own code, there is a straight-forward solution:

List<Car> disjoint = Stream.concat(
bigCarList.stream().filter(c->!smallCarList.contains(c)),
smallCarList.stream().filter(c->!bigCarList.contains(c))
).collect(Collectors.toList());

Just filter one list for all items not contained in the other and vice versa and concatenate both results. That works fairly well for small lists and before consider optimized solutions like hashing or making the result distinct() you should ask yourself why you are using lists if you don’t want neither, duplicates nor a specific order.

It seems like you actually want Sets, not Lists. If you use Sets, Tagir Valeev’s solution is appropriate. But it is not working with the actual semantics of Lists, i.e. doesn’t work if the source lists contain duplicates.


But if you are using Sets, the code can be even simpler:

Set<Car> disjoint = Stream.concat(bigCarSet.stream(), smallCarSet.stream())
.collect(Collectors.toMap(Function.identity(), t->true, (a,b)->null))
.keySet();

This uses the toMap collector which creates a Map (the value is irrelevant, we simply map to true here) and uses a merge function to handle duplicates. Since for two sets, duplicates can only occur when an item is contained in both sets, these are the items we want remove.

The documentation of Collectors.toMap says that the merge function is treated “as supplied to Map.merge(Object, Object, BiFunction)” and we can learn from there, that simply mapping the duplicate pair to null will remove the entry.

So afterwards, the keySet() of the map contains the disjoint set.



Related Topics



Leave a reply



Submit