Skip to content

Collections

Collections is a utility class provided by the JDK, also located in the java.util package. It offers a series of static methods that facilitate operations on various collections.

Note

The name is Collections, not Collection!

We can generally determine the functionality of a method provided by Collections from its name and parameters. For example, for the following static method:

java
public static boolean addAll(Collection<? super T> c, T... elements) { ... }

The addAll() method can add several elements to a collection of type Collection. Since the method signature is Collection, we can pass in various collection types such as List, Set, etc.

Creating Empty Collections

For older versions of the JDK, you can use the following methods provided by Collections to create empty collections:

  • Create an empty List: List<T> emptyList()
  • Create an empty Map: Map<K, V> emptyMap()
  • Create an empty Set: Set<T> emptySet()

It's important to note that the returned empty collections are immutable, meaning you cannot add or remove elements from them.

In newer versions of the JDK (≥9), you can directly use List.of(), Map.of(), and Set.of() to create empty collections.

Creating Single-Element Collections

For older versions of the JDK, Collections provides methods to create single-element collections:

  • Create a single-element List: List<T> singletonList(T o)
  • Create a single-element Map: Map<K, V> singletonMap(K key, V value)
  • Create a single-element Set: Set<T> singleton(T o)

It’s worth noting that the returned single-element collections are also immutable.

In newer versions of the JDK (≥9), you can directly use List.of(T...), Map.of(T...), and Set.of(T...) to create collections with any number of elements.

Sorting

Collections can sort a List. Since sorting directly modifies the position of elements in the List, you must pass a mutable List:

java
import java.util.*;

public class Main {
    public static void main(String[] args) {
        List<String> list = new ArrayList<>();
        list.add("apple");
        list.add("pear");
        list.add("orange");
        // Before sorting:
        System.out.println(list);
        Collections.sort(list);
        // After sorting:
        System.out.println(list);
    }
}

Shuffling

Collections also provides a shuffle algorithm. You can pass an ordered List, which randomly rearranges the order of its internal elements, effectively shuffling the list:

java
import java.util.*;

public class Main {
    public static void main(String[] args) {
        List<Integer> list = new ArrayList<>();
        for (int i = 0; i < 10; i++) {
            list.add(i);
        }
        // Before shuffling:
        System.out.println(list);
        // Shuffle:
        Collections.shuffle(list);
        // After shuffling:
        System.out.println(list);
    }
}

Immutable Collections

Collections also provides a set of methods to wrap mutable collections into immutable collections:

  • Wrap into an immutable List: List<T> unmodifiableList(List<? extends T> list)
  • Wrap into an immutable Set: Set<T> unmodifiableSet(Set<? extends T> set)
  • Wrap into an immutable Map: Map<K, V> unmodifiableMap(Map<? extends K, ? extends V> m)

This wrapping is done by creating a proxy object that intercepts all modification methods. Let’s see the effect:

java
import java.util.*;

public class Main {
    public static void main(String[] args) {
        List<String> mutable = new ArrayList<>();
        mutable.add("apple");
        mutable.add("pear");
        // Convert to immutable collection:
        List<String> immutable = Collections.unmodifiableList(mutable);
        immutable.add("orange"); // UnsupportedOperationException!
    }
}

However, continuing to modify the original mutable List is still allowed, and it will directly affect the wrapped "immutable" List:

java
import java.util.*;

public class Main {
    public static void main(String[] args) {
        List<String> mutable = new ArrayList<>();
        mutable.add("apple");
        mutable.add("pear");
        // Convert to immutable collection:
        List<String> immutable = Collections.unmodifiableList(mutable);
        mutable.add("orange");
        System.out.println(immutable);
    }
}

Therefore, if we want to wrap a mutable List into an immutable List, it's best to immediately discard the reference to the mutable List after returning the immutable List. This way, we can ensure that subsequent operations won’t inadvertently change the original object, leading to modifications in the "immutable" List:

java
import java.util.*;

public class Main {
    public static void main(String[] args) {
        List<String> mutable = new ArrayList<>();
        mutable.add("apple");
        mutable.add("pear");
        // Convert to immutable collection:
        List<String> immutable = Collections.unmodifiableList(mutable);
        // Immediately discard reference to mutable:
        mutable = null;
        System.out.println(immutable);
    }
}

Thread-Safe Collections

Collections also provides a set of methods to turn non-thread-safe collections into thread-safe ones:

  • Convert to a thread-safe List: List<T> synchronizedList(List<T> list)
  • Convert to a thread-safe Set: Set<T> synchronizedSet(Set<T> s)
  • Convert to a thread-safe Map: Map<K, V> synchronizedMap(Map<K, V> m)

We will discuss the concept of multithreading later. Since Java 5 introduced more efficient concurrent collection classes, the above synchronization methods have become less useful.

Summary

The Collections class provides a set of utility methods to facilitate the use of collection classes:

  • Create empty collections.
  • Create single-element collections.
  • Create immutable collections.
  • Perform operations like sorting and shuffling.
Collections has loaded