Skip to main content

11.5 Collections Utility Class

The java.util.Collections class is a collection of static utility methods for working with collections. It provides simple solutions for frequently needed tasks like sorting, searching, reversing, and creating unmodifiable collections.

1. Sorting and Reordering

import java.util.*;

List<Integer> numbers = new ArrayList<>(Arrays.asList(5, 2, 8, 1, 9, 3));

// Sort ascending
Collections.sort(numbers);
System.out.println(numbers); // [1, 2, 3, 5, 8, 9]

// Sort descending (with Comparator)
Collections.sort(numbers, Comparator.reverseOrder());
System.out.println(numbers); // [9, 8, 5, 3, 2, 1]

// Reverse order of elements
Collections.reverse(numbers);
System.out.println(numbers); // [1, 2, 3, 5, 8, 9]

// Shuffle randomly
Collections.shuffle(numbers);
System.out.println(numbers); // random order
Collections.shuffle(numbers, new Random(42)); // reproducible with seed

// Rotate (shift left by 2)
List<String> letters = new ArrayList<>(Arrays.asList("A", "B", "C", "D", "E"));
Collections.rotate(letters, -2); // rotate left by 2
System.out.println(letters); // [C, D, E, A, B]

// Swap two elements
Collections.swap(letters, 0, 4);
System.out.println(letters); // element at index 0 and 4 swapped

2. Searching and Statistics

List<Integer> sorted = Arrays.asList(1, 3, 5, 7, 9, 11);

// Binary search (list must be sorted!)
int idx = Collections.binarySearch(sorted, 7);
System.out.println(idx); // 3 (index)

// Min and max
System.out.println(Collections.min(sorted)); // 1
System.out.println(Collections.max(sorted)); // 11

// Frequency of a specific element
List<String> fruits = Arrays.asList("apple", "banana", "apple", "strawberry", "apple");
System.out.println(Collections.frequency(fruits, "apple")); // 3

// Check if collection contains all elements of another
List<String> subset = Arrays.asList("apple", "strawberry");
System.out.println(fruits.containsAll(subset)); // true

3. Creating Unmodifiable (Immutable) Collections

Creates read-only collections that cannot be modified externally. Attempting modification throws UnsupportedOperationException.

// Wrap an existing collection as unmodifiable
List<String> mutable = new ArrayList<>(Arrays.asList("A", "B", "C"));
List<String> immutable = Collections.unmodifiableList(mutable);

// immutable.add("D"); // UnsupportedOperationException!
System.out.println(immutable.get(0)); // "A" (reads are fine)

Map<String, Integer> map = new HashMap<>();
map.put("one", 1); map.put("two", 2);
Map<String, Integer> readOnlyMap = Collections.unmodifiableMap(map);

Factory Methods (Java 9+)

A more concise way to create unmodifiable collections:

// List.of - immutable list (no nulls allowed)
List<String> list = List.of("Alice", "Bob", "Charlie");
// list.add("Dave"); // throws!

// Set.of - immutable set (no duplicates, no guaranteed order)
Set<Integer> set = Set.of(1, 2, 3, 4, 5);

// Map.of - immutable map (key-value pairs)
Map<String, Integer> scoreMap = Map.of(
"Alice", 95,
"Bob", 87,
"Carol", 92
);

// Map.entry + Map.ofEntries (for more than 10 entries)
Map<String, Integer> bigMap = Map.ofEntries(
Map.entry("A", 1),
Map.entry("B", 2),
Map.entry("C", 3)
// ... no upper limit
);

4. Special-Purpose Collections

// Empty immutable collections (return instead of null)
List<String> emptyList = Collections.emptyList();
Set<String> emptySet = Collections.emptySet();
Map<String, Integer> emptyMap = Collections.emptyMap();

// Single-element immutable collections
List<String> singleList = Collections.singletonList("only");
Set<String> singleSet = Collections.singleton("only");

// List of N copies of the same element (useful for initialization)
List<Integer> zeros = Collections.nCopies(5, 0);
System.out.println(zeros); // [0, 0, 0, 0, 0]

5. Thread-Safe Collections

// Wrap existing collections to make thread-safe (legacy approach)
List<String> syncList = Collections.synchronizedList(new ArrayList<>());
Map<String, Integer> syncMap = Collections.synchronizedMap(new HashMap<>());

// Modern approach: use java.util.concurrent (recommended)
import java.util.concurrent.*;
ConcurrentHashMap<String, Integer> concurrentMap = new ConcurrentHashMap<>();
CopyOnWriteArrayList<String> cowList = new CopyOnWriteArrayList<>();

6. Lambda-Friendly Collection Utilities (Java 8+)

List<String> names = new ArrayList<>(Arrays.asList("Alice", "Bob", "Charlie", "Dave"));

// removeIf: remove elements matching a predicate
names.removeIf(name -> name.length() < 4);
System.out.println(names); // [Alice, Charlie, Dave]

// replaceAll: transform every element in-place
names.replaceAll(String::toUpperCase);
System.out.println(names); // [ALICE, CHARLIE, DAVE]

// sort with Comparator
names.sort(Comparator.naturalOrder()); // ascending
names.sort(Comparator.reverseOrder()); // descending
names.sort(Comparator.comparingInt(String::length)); // by length

// Chained comparator: sort by length descending, then alphabetically
names.sort(Comparator.comparingInt(String::length)
.reversed()
.thenComparing(Comparator.naturalOrder()));

// Map utility methods (Java 8+)
Map<String, Integer> scores = new HashMap<>();
scores.put("Alice", 90);
scores.put("Bob", 85);

scores.getOrDefault("Carol", 0); // 0 if Carol is absent
scores.putIfAbsent("Alice", 100); // ignored because Alice exists
scores.computeIfAbsent("Dave", k -> 75); // adds Dave with score 75
scores.merge("Alice", 5, Integer::sum); // Alice's score += 5
scores.forEach((name, score) ->
System.out.println(name + ": " + score));

Pro Tips

Immutable collections vs defensive copying:

  • List.of(), Map.of() etc. (Java 9+ factory methods): truly immutable— throws exception on modification
  • Collections.unmodifiableList(): just a wrapper — if the underlying collection changes, the view changes too
List<String> original = new ArrayList<>(List.of("A", "B"));
List<String> unmod = Collections.unmodifiableList(original);

original.add("C"); // modify the original
System.out.println(unmod); // [A, B, C] -- the view changed too!

// Truly immutable copy
List<String> trulyImmutable = List.copyOf(original); // Java 10+

When returning a collection from a method, return Collections.emptyList() instead of null. This prevents NullPointerExceptions and keeps the calling code clean.