Every Java collection implements the equals() method, which compares it with another collection. In the case of Set, two sets are considered equal (set1.equals(set2) returns true) when:
- Each collection is of type Set
- They have the same size
- Each element of one set is contained in another set
The following code illustrates the definition:
Set<String> set1 = new HashSet<>();
set1.add("s1");
set1.add("s2");
List<String> list = new ArrayList<>();
list.add("s2");
list.add("s1");
System.out.println(set1.equals(list)); //prints: false
The preceding collections are not equal because they are of different types. Now, let's compare two sets:
Set<String> set2 = new HashSet<>();
set2.add("s3");
set2.add("s1");
System.out.println(set1.equals(set2)); //prints: false
set2.remove("s3");
set2.add("s2");
System.out.println(set1.equals(set2)); //prints: true
The preceding sets are equal or not depending on the composition of their elements, even if the size of the sets is the same.
If two sets are equal, their hashCode() methods return the same integer value. But the equality of the hashCode() result does not guarantee that the sets are equal. We have talked about the reason for that while discussing the hashCode() method's implementation in the preceding subsection Implement equals() and hashCode().
The containsAll(Collection) method of the Set interface (or any collection that implements the Collection interface for that matter) returns true only if all elements of the provided collection are present in the set. If the size of the set and the size of the provided collection are equal, we can be sure that the same (well, equal) elements compose each of the compared collections. It does not guarantee though that the elements are of the same type, because they might be children of different generations with the equals() method implemented only in the common parent.
If not, we can find the difference using the methods retainAll(Collection) and removeAll(Collection), described earlier in this section. Let's assume we have two lists as follows:
Set<String> set1 = new HashSet<>();
set1.add("s1");
set1.add("s1");
set1.add("s2");
set1.add("s3");
set1.add("s4");
Set<String> set2 = new HashSet<>();
set2.add("s1");
set2.add("s2");
set2.add("s2");
set2.add("s5");
We can find which elements in one set are not present in the other:
Set<String> set = new HashSet<>(set1);
set.removeAll(set2);
System.out.println(set); //prints: [s3, s4]
set = new HashSet<>(set2);
set.removeAll(set1);
System.out.println(set); //prints: [s5]
Notice how we have created a temporary set to avoid corruption of the original one.
Since Set does not allow duplicate elements, there is no need to use the retainAll(Collection) method for the purpose of finding more differences between the sets, like we did for List. Instead, the retainAll(Collection) method can be used to find common elements in two sets:
Set<String> set = new HashSet<>(set1);
set.retainAll(set2);
System.out.println(set); //prints: [s1, s2]
set = new HashSet<>(set2);
set.retainAll(set1);
System.out.println(set); //prints: [s1, s2]
As you can from the preceding code, to find the common elements between two sets, it is enough to use the retainAll() method only once, no matter which set is the main and which one is used as a parameter.
Also, please notice that neither the retainAll(Collection) nor the removeAll(Collection) method guarantee that the compared set and the collection passed in contain elements of the same type. They might be a mix of children with a common parent that have the equals() method implemented in the parent only, and the parent type is the type of the set and the collection passed in.