Sets – Collections: Part II
15.4 Sets
The Java Collections Framework provides the Set<E> interface to model the mathematical set abstraction.
The Set<E> Interface
Unlike other implementations of the Collection<E> interface, implementations of the Set<E> interface do not allow duplicate elements. The Set<E> interface does not define any new methods, and its add() and addAll() methods will not store duplicates. If an element is not currently in the set, two consecutive calls to the add() method to insert the element will first return true, then false. A Set<E> models a mathematical set (see Table 15.3)—that is, it is an unordered collection of distinct objects.
Table 15.3 Bulk Operations and Set Logic
Set methods (a and b are sets) | Corresponding mathematical operations |
a.containsAll(b) | b ⊆ a (subset) |
a.addAll(b) | a = a ∪ b (union) |
a.removeAll(b) | a = a – b (difference) |
a.retainAll(b) | a = a ∩ b (intersection) |
a.clear() | a = ∅ (empty set) |
Multisets (also called bags) that allow duplicate elements cannot be implemented using the Set<E> interface, since this interface requires that elements are unique in the collection. An implementation of the Set<E> interface can choose to allow the null value, but the concrete class TreeSet<E> does not.
Creating Unmodifiable Sets
Unmodifiable collections are useful to prevent a collection from accidently being modified, as doing so might cause the program to behave incorrectly.
Creating and using unmodifiable sets is very similar to creating and using unmodifiable lists. Not surprisingly, the discussion here on unmodifiable sets reflects the discussion on unmodifiable lists (§12.2, p. 649). Later we will discuss unmodifiable maps (p. 832) and unmodifiable view collections (p. 856).
The Set<E> interface provides generic static methods to create unmodifiable sets that have the following characteristics:
- An unmodifiable set cannot be modified structurally; for example, elements cannot be added, removed, or replaced in such a set. Any such attempt will result in an UnsupportedOperationException to be thrown. However, if the elements themselves are mutable, the elements may appear modified.
- Both duplicates and null elements are not allowed, and will result in a NullPointerException if an attempt is made to create them with such elements.
- The order of the elements in such a set is unspecified.
- Such a set can be serialized if its elements are serializable (§20.5, p. 1261).
static <E> Set<E> of(E e1, E e2, E e3, E e4, E e5,
E e6, E e7, E e8, E e9, E e10)
This method is overloaded, accepting any number of elements from 0 to 10. It returns an unmodifiable set containing the number of elements specified. It throws a NullPointerException if an element is null.
@SafeVarargs static <E> Set<E> of(E… elements)
This variable arity method returns an unmodifiable set containing an arbitrary number of elements. It throws a NullPointerException if an element is null or if the array is null. The annotation suppresses the heap pollution warning in its declaration and unchecked generic array creation warning at the call sites.
static <E> Set<E> copyOf(Collection<? extends E> collection)
This generic method returns an unmodifiable set containing the elements of the specified collection, in its iteration order. The specified collection must not be null, and it must not contain any null elements—otherwise, a NullPointer-Exception is thrown. If the specified collection is subsequently modified, the returned set will not reflect such modifications.
The code below shows that a set created by the Set.of() method cannot be modified. The set returned is also not an instance of the class HashSet.
Set<String> set = Set.of(“Tom”, “Dick”, “Harriet”);
// set.add(“Harry”); // UnsupportedOperationException
// set.remove(2); // UnsupportedOperationException
System.out.println(set); // [Harriet, Tom, Dick]
System.out.println(set instanceof HashSet); // false
The Set.of() method does not allow null elements:
Set<String> coinSet = Set.of(“dime”, “nickel”, null); // NullPointerException
For arguments up to 10, an appropriate fixed-arity Set.of() method is called. Above 10 arguments, the variable arity Set.of(E…) method is called, passing an implicitly created array containing the arguments.
Set<Integer> intSet1 = Set.of(1, 2, 3, 4, 5, 6, 7, 8, 9, 10); // Fixed-arity
Set<Integer> intSet2 = Set.of(1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11); // Varargs
System.out.println(intSet1); // [9, 8, 7, 6, 5, 4, 3, 2, 1, 10]
System.out.println(intSet2); // [7, 6, 5, 4, 3, 2, 1, 11, 10, 9, 8]
At (1) below, an explicit array is passed as an argument, resulting in the variable arity Set.of(E…) method being called, creating a set of String. At (2), the method call explicitly specifies the type of its argument as String[]. In this case the one-argument Set.of(E) method is called, creating a set of size 1 and whose element type is String[].
String[] strArray = {“Tom”, “Dick”, “Harriet”};
Set<String> strSet = Set.of(strArray); // (1) Set of String
Set<String[]> strArraySet = Set.<String[]>of(strArray); // (2) Set of String[]
System.out.println(strSet); // [Harriet, Dick, Tom]
System.out.println(strArraySet); // [[Ljava.lang.String;@3b22cdd0]
The code below shows how we can make a copy of a collection, in this case, a set. The copyOf() method creates a copy of the set passed as an argument at (1). The set created is unmodifiable analogous to the sets created with the Set.of() methods. The code also shows that modifying the original set does not reflect in the copy of the set.
Set<String> fab4 = new HashSet<>();
fab4.add(“John”); fab4.add(“Paul”); fab4.add(“George”); fab4.add(“Ringo”);
System.out.println(fab4); // [George, John, Ringo, Paul]
Set<String> fabAlways = Set.copyOf(fab4); // (1)
fab4.remove(“John”); fab4.remove(“George”); // Modify original set
System.out.println(fab4); // [Ringo, Paul]
System.out.println(fabAlways); // [John, Paul, Ringo, George]