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.
Override
hashcode
andequals
inPOJO
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();
}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 Set
s, not List
s. If you use Set
s, Tagir Valeev’s solution is appropriate. But it is not working with the actual semantics of List
s, i.e. doesn’t work if the source lists contain duplicates.
But if you are using Set
s, 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
How to Disable or Bypass Hardware Graphics Acceleration(Prism) in Javafx
Log4J: How to Use Socketappender
How to Keep the Iteration Order of a List When Using Collections.Tomap() on a Stream
JSONmanagedreference VS JSONbackreference
Best Way to Get Geo-Location in Java
Validation of a List of Objects in Spring
Converting Array of Primitives to Array of Containers in Java
Rotating Bufferedimage Instances
How to Use "Cd" Command Using Java Runtime
Java - Reading, Manipulating and Writing Wav Files
Java Interfaces Methodology: Should Every Class Implement an Interface
Javafx Using Objects from Maincontroller or Other Controllers in Proper Controller Class