The Collections Class – Collections: Part II
By Jaime Williams / November 23, 2022 / No Comments / Introduction to Streams, Oracle Certification Exam, The NavigableSet Interface
15.11 The Collections Class
The Java Collections Framework also contains two utility classes, Collections and Arrays, that provide various operations on collections and arrays, such as algorithms for sorting and searching, or creating customized collections. Practically any operation on a collection can be done using the methods provided by this framework.
The methods also throw a NullPointerException if the specified collection or array references passed to them are null.
The utility methods declared in the Collections class are all public and static; therefore, these two modifiers will be omitted in their method header declarations in this section.
Unmodifiable Views of Collections
Unmodifiable views of collections are created by the utility methods unmodifiableInterface() in the Collections class, where Interface can be any of the core interfaces in the Java Collections Framework. The name of these methods is a slight misnomer, as they create unmodified views of collections on the underlying or backing collection that is passed as an argument to the method.
<E> Collection<E> unmodifiableCollection(Collection<? extends E> c)
<E> List<E> unmodifiableList(List<? extends E> list)
<E> Set<E> unmodifiableSet(Set<? extends E> set)
<E> SortedSet<E> unmodifiableSortedSet(SortedSet<E> sortedSet)
<E> NavigableSet<E> unmodifiableNavigableSet(NavigableSet<E> navSet)
Return an unmodifiable view of the collection passed as an argument.
Query operations on the returned collection are delegated to the underlying collection, and changes in the underlying collection are reflected in the unmodifiable view.
Any attempt to modify the view of a collection will result in an Unsupported-OperationException.
The view collection returned by the unmodifiableCollection() method does not delegate the equals() and the hashCode() methods to the backing collection. Instead, the returned view uses the corresponding methods inherited from the Object class. This is to safeguard the contract of these methods when the backing collection is a set or a list. However, the views returned by the other methods above do not exhibit this behavior.
<K, V> Map<K, V> unmodifiableMap(Map<? extends K, ? extends V> map)
<K, V> SortedMap<K, V> unmodifiableSortedMap(SortedMap<K, ? extends V> sm)
<K, V> NavigableMap<K, V>
unmodifiableNavigableMap(NavigableMap<K, ? extends V> nm)
Return an unmodifiable view of the map passed as an argument.
Query operations on the view of the map are delegated to the underlying map, and changes in the underlying map are reflected in the unmodifiable view.
Any attempt to modify the returned map will result in an Unsupported-OperationException.
An unmodifiable view of a collection should not be confused with an unmodifiable collection. They are both unmodifiable—that is, they are read-only collections. No operations that can change them structurally are permitted (the famous UnsupportedOperationException). However, an unmodifiable view of a collection is backed by an underlying collection, but that is not the case for an unmodifiable collection. In the case of an unmodifiable view of a collection, any changes to the underlying collection are reflected in the unmodifiable view.
Examples of unmodified collections include the following:
- Unmodifiable lists created by List.of() and List.copyOf() methods (§12.2, p. 649). See also the comparison of unmodifiable lists and list views that are created by the Arrays.asList() method (§12.7, p. 660).
- Unmodifiable sets created by Set.of() and Set.copyOf() methods (p. 804).
- Unmodifiable maps created by Map.of(), Map.ofEntries(), and Map.copyOf() methods (p. 832).
The code below creates an unmodifiable view of a map whose values are mutable.
// Mutable courses:
StringBuilder mc1 = new StringBuilder(“Java I”);
StringBuilder mc2 = new StringBuilder(“Java II”);
StringBuilder mc3 = new StringBuilder(“Java III”);
StringBuilder mc4 = new StringBuilder(“Java IV”);
// Backing map:
Map<Integer, StringBuilder> backingMap = new HashMap<>();
backingMap.put(200, mc1); backingMap.put(300, mc2);
backingMap.put(400, mc3); backingMap.put(500, mc4);
// Unmodifiable view of a map:
Map<Integer, StringBuilder> unmodViewMap
= Collections.unmodifiableMap(backingMap);
As in the case of unmodifiable collections, the code below throws an Unsupported-OperationException when an attempt is made to structurally change the unmodifiable view of a map.
// UnsupportedOperationException at (1), (2), and (3):
unmodViewMap.put(100, new StringBuilder(“Java Now”));
unmodViewMap.remove(400);
unmodViewMap.replace(200, new StringBuilder(“First Java”));
However, changes to the backing map are reflected in the unmodifiable view:
backingMap.remove(200); backingMap.remove(400);
System.out.println(“Backing map: ” + backingMap);
System.out.println(“Unmodifiable view: ” + unmodViewMap);
// Backing map: {500=Java Complete, 300=Java II}
// Unmodifiable view: {500=Java Complete, 300=Java II}
Since the values in the unmodifiable view of a map are mutable, the code below shows that changing a value of an entry in the unmodifiable view is reflected in the backing map.
StringBuilder mutableCourse = unmodViewMap.get(500); // Get the course.
mutableCourse.replace(5, 8, “Complete”); // Change the course name.
System.out.println(“Backing map: ” + backingMap);
System.out.println(“Unmodifiable view: ” + unmodViewMap);
// Backing map: {400=Java III, 500=Java Complete, 200=Java I, 300=Java II}
// Unmodifiable view: {400=Java III, 500=Java Complete, 200=Java I, 300=Java II}