245

So if I have two sets:

Set<Integer> test1 = new HashSet<Integer>();
test1.add(1);
test1.add(2);
test1.add(3);

Set<Integer> test2 = new HashSet<Integer>();
test2.add(1);
test2.add(2);
test2.add(3);
test2.add(4);
test2.add(5);

Is there a way to compare them and only have a set of 4 and 5 returned?

sergeyan
  • 1,173
  • 1
  • 14
  • 28
David Tunnell
  • 7,252
  • 20
  • 66
  • 124

11 Answers11

284

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.

Prabhaker A
  • 8,317
  • 1
  • 18
  • 24
  • 61
    This will work but I think it would be a nice feature to have the set operations like union , difference built in java. The above solution will modify the set , in many situations we don't really want that. – Praveen Kumar Jul 18 '14 at 08:09
  • 193
    How can Java have the gall to call this data structure a `Set` when it doesn't define `union`, `intersection` or `difference`!!! – James Newman Dec 14 '15 at 21:16
  • 14
    This solution is not fully correct. Because the order of test1 and test2 makes a difference. – Bojan Petkovic Nov 01 '16 at 22:34
  • 1
    Would `test1.removeAll(test2);` return the same result as `test2.removeAll(test1);` ? – datv Dec 03 '17 at 14:13
  • 3
    @datv The result would be different. `test1.removeAll(test2)` is an empty set. `test2.removeAll(test1)` is `{4, 5}`. – silentwf Dec 10 '17 at 14:11
  • 5
    @BojanPetkovic This gives the elements of `test2` that are not present in `test1`, which is exactly what the OP is asking for. – augurar Mar 09 '18 at 21:07
  • 2
    This is extra useful combined with double braces: `new HashSet(set1) {{ removeAll(set2); }};` – Chris Pfohl Jun 02 '20 at 19:27
  • The IDE gives a hint that "Call to 'set.removeAll(list)' may work slowly ", this can be replaced to `test1.forEach(test2::remove)` – kai Dec 27 '21 at 11:45
153

If you use Guava (former Google Collections) library there is a solution:

SetView<Number> difference = com.google.common.collect.Sets.difference(test2, test1);

The returned SetView is a Set, it is a live representation you can either make immutable or copy to another set. test1 and test2 are left intact.

Mikhail Golubtsov
  • 6,285
  • 3
  • 29
  • 36
  • 15
    Note that the [order](https://google.github.io/guava/releases/19.0/api/docs/com/google/common/collect/Sets.html#difference(java.util.Set,%20java.util.Set)) of test2 and test1 matters. There also is [symmetricDifference()](https://google.github.io/guava/releases/19.0/api/docs/com/google/common/collect/Sets.html#symmetricDifference(java.util.Set,%20java.util.Set)) where the order doesn't matter. – datv Dec 03 '17 at 14:22
  • 2
    `symmetricDifference()` will bring all but the intersection, that's not what the original question asked for. – Allenaz Aug 02 '19 at 16:01
24

You can use Apache Commons CollectionUtils.disjunction to get all differences, or CollectionUtils.subtract to get the difference in the first collection.

<dependency>
    <groupId>org.apache.commons</groupId>
    <artifactId>commons-collections4</artifactId>
    <version>4.4</version>
</dependency>

Here is an example of how to do that:

import org.apache.commons.collections4.CollectionUtils;
import java.util.List;

var collection1 = List.of(-1, 0, 1, 2, 3, 4, 5);
var collection2 = List.of(       1, 2, 3, 4, 5, 6, 7, 8, 9);

// [-1, 0, 1, 2, 3, 4, 5]
System.out.println(collection1);
// [1, 2, 3, 4, 5, 6, 7, 8, 9]
System.out.println(collection2);

// [-1, 0]
System.out.println(CollectionUtils.subtract(collection1, collection2));
// [6, 7, 8, 9]
System.out.println(CollectionUtils.subtract(collection2, collection1));

// [1, 2, 3, 4, 5]
System.out.println(CollectionUtils.retainAll(collection1, collection2));
// [1, 2, 3, 4, 5]
System.out.println(CollectionUtils.retainAll(collection2, collection1));

// [-1, 0, 1, 1, 2, 2, 3, 3, 4, 4, 5, 5, 6, 7, 8, 9]
System.out.println(CollectionUtils.collate(collection1, collection2));
// [-1, 0, 1, 1, 2, 2, 3, 3, 4, 4, 5, 5, 6, 7, 8, 9]
System.out.println(CollectionUtils.collate(collection2, collection1));

// [-1, 0, 6, 7, 8, 9]
System.out.println(CollectionUtils.disjunction(collection1, collection2));
// [-1, 0, 6, 7, 8, 9]
System.out.println(CollectionUtils.disjunction(collection2, collection1));

// [1, 2, 3, 4, 5]
System.out.println(CollectionUtils.intersection(collection1, collection2));
// [1, 2, 3, 4, 5]
System.out.println(CollectionUtils.intersection(collection2, collection1));

// [-1, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
System.out.println(CollectionUtils.union(collection1, collection2));
// [-1, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
System.out.println(CollectionUtils.union(collection2, collection1));
Letfar
  • 3,253
  • 6
  • 25
  • 35
pwipo
  • 463
  • 3
  • 7
21

Yes:

test2.removeAll(test1)

Although this will mutate test2, so create a copy if you need to preserve it.

Also, you probably meant <Integer> instead of <int>.

arshajii
  • 127,459
  • 24
  • 238
  • 287
21

Java 8

We can make use of removeIf which takes a predicate to write a utility method as:

// computes the difference without modifying the sets
public static <T> Set<T> differenceJava8(final Set<T> setOne, final Set<T> setTwo) {
     Set<T> result = new HashSet<T>(setOne);
     result.removeIf(setTwo::contains);
     return result;
}

And in case we are still at some prior version then we can use removeAll as:

public static <T> Set<T> difference(final Set<T> setOne, final Set<T> setTwo) {
     Set<T> result = new HashSet<T>(setOne);
     result.removeAll(setTwo);
     return result;
}
akhil_mittal
  • 23,309
  • 7
  • 96
  • 95
12

Adding a solution which I've recently used myself and haven't seen mentioned here. If you have Apache Commons Collections available then you can use the SetUtils#difference method:

// Returns all the elements of test2 which are not in test1
SetUtils.difference(test2, test1) 

Note that according to the documentation the returned set is an unmodifiable view:

Returns a unmodifiable view containing the difference of the given Sets, denoted by a \ b (or a - b). The returned view contains all elements of a that are not a member of b.

Full documentation: https://commons.apache.org/proper/commons-collections/apidocs/org/apache/commons/collections4/SetUtils.html#difference-java.util.Set-java.util.Set-

Matt Watson
  • 5,065
  • 4
  • 30
  • 48
5

Just to put one example here (system is in existingState, and we want to find elements to remove (elements that are not in newState but are present in existingState) and elements to add (elements that are in newState but are not present in existingState) :

public class AddAndRemove {

  static Set<Integer> existingState = Set.of(1,2,3,4,5);
  static Set<Integer> newState = Set.of(0,5,2,11,3,99);

  public static void main(String[] args) {

    Set<Integer> add = new HashSet<>(newState);
    add.removeAll(existingState);

    System.out.println("Elements to add : " + add);

    Set<Integer> remove = new HashSet<>(existingState);
    remove.removeAll(newState);

    System.out.println("Elements to remove : " + remove);

  }
}

would output this as a result:

Elements to add : [0, 99, 11]
Elements to remove : [1, 4]
Bojan Vukasovic
  • 2,054
  • 22
  • 43
3

The order is important, since you need 4 and 5 which is int (not Integer), Use test2.removeAll(test1).

HseJaR
  • 31
  • 9
1

If you are using Java 8, you could try something like this:

public Set<Number> difference(final Set<Number> set1, final Set<Number> set2){
    final Set<Number> larger = set1.size() > set2.size() ? set1 : set2;
    final Set<Number> smaller = larger.equals(set1) ? set2 : set1;
    return larger.stream().filter(n -> !smaller.contains(n)).collect(Collectors.toSet());
}
Josh M
  • 11,611
  • 7
  • 39
  • 49
  • 6
    @Downvoter: Perhaps you have failed to realize that the other answers don't check to see which `Set` is larger... Therefore, if you are trying to subtract a a smaller `Set` from a larger `Set`, you will receive different results. – Josh M Sep 05 '13 at 20:06
  • 45
    you are assuming that the consumer of that function always wants to subtract the smaller set. Set difference is anticommutative (http://en.wikipedia.org/wiki/Anticommutativity). A-B != B-A – Simon Feb 12 '15 at 15:09
  • 7
    Regardless which variant of difference you implement, I would use `public static Set difference(final Set set1, final Set set2) {`as signature, the method is then usable as generic utility function. – kap Jan 12 '16 at 10:18
  • 1
    @kap but then add a `Comparator` to be able to customize the comparison because `equals` is not always enough. – gervais.b Jul 14 '16 at 14:18
  • 7
    This will lead to unexpected results as the order of the difference operation may be switched without the user being aware. Subtraction of a larger set from a smaller set is mathematically well-defined and there are plenty of use cases for it. – Joel Cornett Dec 29 '16 at 19:35
  • 2
    set difference is not a commutative operation. the order of the operands matters. The OP question and comments indicates that he wants to do test2 - test1. your answer is missleading... at least if it were the symetric difference, but it isn't – Radu Simionescu Mar 06 '17 at 14:20
  • 2
    This is very misleading. I can imagine this working if the smaller set only contained items in the larger set, but unfortunately that doesn't cover every use case. Gotta think outside the box. Set difference has nothing to do with the size, so you're just taking a difference and switching the order arbitrarily. I downvoted this as well. – Josh Wyant Oct 25 '18 at 18:37
1

You can make a union using .addAll(), and an intersection using .retainAll(), of the two sets and use .removeIf(), to remove the intersection (or the duplicated element) from the union.

HashSet union = new HashSet(group1);
union.addAll(group2);
        
System.out.println("Union: " + union);
        
HashSet intersection = new HashSet(group1);
intersection.retainAll(group2);
        
System.out.println("Intersection: " + intersection);
        
HashSet difference = new HashSet(union);
difference.removeIf(n -> (difference.contains(intersection)));
        
System.out.println("Difference: " + difference);
PIG208
  • 2,060
  • 2
  • 10
  • 25
0

Leaning on the answer by Bojan Vukasovic here is a more complete answer.

It actually returns a delta providing the list of elements added and remove.

The order you pass the sets to the delta method is important.

To obtain a delta:

Set<Integer> test1 = new HashSet<Integer>();
test1.add(1);
test1.add(2);
test1.add(3);
test1.add(9);

Set<Integer> test2 = new HashSet<Integer>();
test2.add(1);
test2.add(2);
test2.add(3);
test2.add(4);
test2.add(5);

var delta = Sets.delta(test1, test2);

delta.additions.equals(Set.of(4,5));
delta.subtractions.equals(Set.of(9));
package dev.onepub.util;

import java.util.HashSet;
import java.util.Set;

public class Sets
{
    /**
     * Calculates the delta of the two sets.
     * 
     * The method assumes that you have an existing set
     * that has been revised.
     * 
     * The delta is expressed as:
     *  additions - the list of items FOUND in the revised set, that are not in the existing set.
     *  subtractions - the list of items NOT in the revised set, that are in the existing set.
     *  
     * The existing and revised sets are left unchanged.
     * 
     * @param <T>
     * @param existing - the original set used as the baseline 
     * @param revised - the revised set which will be compared against the existing set.
     * @return a SetDelta that describes the set of changes.
     */
    static public <T> SetDelta<T> delta(Set<T> existing, Set<T> revised)
    {
        var additions = new HashSet<>(revised);
        additions.removeAll(existing);

        var subtractions = new HashSet<>(existing);
        subtractions.removeAll(revised);

        return new SetDelta<T>(additions, subtractions);
    }
    
    /**
     * Describes the changes between two sets. 
     * @author bsutton
     *
     * @param <T> the type of entity held by the set.
     */
    static public class SetDelta<T>
    {
        public SetDelta(HashSet<T> additions, HashSet<T> subtractions)
        {
            this.additions = additions;
            this.subtractions = subtractions;
        }

        // The set of additional items
        final public Set<T> additions;
        
        // The set of removed items.
        final public Set<T> subtractions;

    }
}


And the unit tests

package dev.onepub.util;

import static org.junit.jupiter.api.Assertions.*;

import java.util.Set;

import org.junit.jupiter.api.Test;

class SetsTest
{

    @Test
    void test()
    {
        Set<Integer> existingState = Set.of(1, 2, 3, 4, 5);
        Set<Integer> newState = Set.of(0, 5, 2, 11, 3, 99);

        var delta = Sets.delta(existingState, newState);

        assertEquals(delta.additions, Set.of(0, 99, 11));
        assertEquals(delta.subtractions, Set.of(1, 4));
    }

    
    @Test
    void testSame()
    {
        Set<Integer> existingState = Set.of(1,2);
        Set<Integer> newState = Set.of(1,2);

        var delta = Sets.delta(existingState, newState);

        assertEquals(delta.additions, Set.of());
        assertEquals(delta.subtractions, Set.of());
    }
    
    @Test
    void testBothEmpty()
    {
        Set<Integer> existingState = Set.of();
        Set<Integer> newState = Set.of();

        var delta = Sets.delta(existingState, newState);

        assertEquals(delta.additions, Set.of());
        assertEquals(delta.subtractions, Set.of());
    }
    
    @Test
    void testAddOne()
    {
        Set<Integer> existingState = Set.of();
        Set<Integer> newState = Set.of(1);

        var delta = Sets.delta(existingState, newState);

        assertEquals(delta.additions, Set.of(1));
        assertEquals(delta.subtractions, Set.of());
    }
    
    @Test
    void testRemoveOne()
    {
        Set<Integer> existingState = Set.of(1);
        Set<Integer> newState = Set.of();

        var delta = Sets.delta(existingState, newState);

        assertEquals(delta.additions, Set.of());
        assertEquals(delta.subtractions, Set.of(1));
    }
    
    @Test
    void testAddOneRemoveOne()
    {
        Set<Integer> existingState = Set.of(1);
        Set<Integer> newState = Set.of(0);

        var delta = Sets.delta(existingState, newState);

        assertEquals(delta.additions, Set.of(0));
        assertEquals(delta.subtractions, Set.of(1));
    }

}

Brett Sutton
  • 3,900
  • 2
  • 28
  • 53