Collections Utility Class
The Collections class (note the s — not to be confused with the Collection interface) is a treasure chest of static utility methods that operate on lists, sets, and maps. Think of it as the Swiss Army knife that comes free with every Java installation.
What Is the Collections Class?
java.util.Collections is a final class with a private constructor — you can never instantiate it. Every method is static, so you call them directly on the class:
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
public class QuickDemo {
public static void main(String[] args) {
List<Integer> numbers = new ArrayList<>(List.of(5, 2, 8, 1, 9, 3));
Collections.sort(numbers);
System.out.println(numbers);
}
}
Output:
[1, 2, 3, 5, 8, 9]
Note:
Collectionsworks on any class that implementsCollectionorList. Most methods accept aList<T>orCollection<T>parameter, not a raw array. For array utilities, see theArraysclass.
Sorting
sort()
List<String> names = new ArrayList<>(List.of("Zara", "Alice", "Mike", "Bob"));
Collections.sort(names); // natural (alphabetical) order
System.out.println(names);
Collections.sort(names, Comparator.reverseOrder()); // reverse order
System.out.println(names);
Output:
[Alice, Bob, Mike, Zara]
[Zara, Mike, Bob, Alice]
You can supply your own Comparator as the second argument to sort by any custom rule — for example, by string length:
Collections.sort(names, Comparator.comparingInt(String::length));
reverseOrder()
Collections.reverseOrder() returns a Comparator that imposes the reverse of natural ordering. It’s handy when sorting in descending order without writing a lambda:
List<Integer> scores = new ArrayList<>(List.of(88, 72, 95, 60));
Collections.sort(scores, Collections.reverseOrder());
System.out.println(scores); // [95, 88, 72, 60]
Searching
binarySearch()
binarySearch() finds an element in a sorted list using the binary search algorithm, returning the index if found or a negative value if not.
List<Integer> sorted = new ArrayList<>(List.of(10, 20, 30, 40, 50));
int idx = Collections.binarySearch(sorted, 30);
System.out.println("Found at index: " + idx); // Found at index: 2
Warning: The list must be sorted before calling
binarySearch(). Calling it on an unsorted list produces undefined results.
Shuffling and Reversing
shuffle()
Randomly rearranges the elements of a list — useful for card games, quiz randomization, or any scenario requiring random ordering:
List<String> deck = new ArrayList<>(List.of("Ace", "King", "Queen", "Jack", "10"));
Collections.shuffle(deck);
System.out.println(deck); // e.g. [Queen, 10, Ace, Jack, King]
You can optionally pass a Random instance for reproducible results:
Collections.shuffle(deck, new java.util.Random(42));
reverse()
Reverses the order of elements in-place:
List<Integer> nums = new ArrayList<>(List.of(1, 2, 3, 4, 5));
Collections.reverse(nums);
System.out.println(nums); // [5, 4, 3, 2, 1]
rotate()
Shifts elements by a given distance. Positive distance rotates right; negative rotates left:
List<String> items = new ArrayList<>(List.of("A", "B", "C", "D", "E"));
Collections.rotate(items, 2);
System.out.println(items); // [D, E, A, B, C]
Min and Max
List<Integer> values = List.of(3, 1, 4, 1, 5, 9, 2, 6);
System.out.println(Collections.min(values)); // 1
System.out.println(Collections.max(values)); // 9
Both methods also accept a Comparator if your objects don’t implement Comparable or you want a custom rule:
List<String> words = List.of("apple", "fig", "banana", "kiwi");
String shortest = Collections.min(words, Comparator.comparingInt(String::length));
System.out.println(shortest); // fig
Filling and Copying
fill()
Replaces every element in a list with the specified value — handy for resetting a list:
List<String> slots = new ArrayList<>(List.of("X", "X", "X", "X"));
Collections.fill(slots, "-");
System.out.println(slots); // [-, -, -, -]
copy()
Copies all elements from a source list into a destination list. The destination must be at least as large as the source:
List<String> src = List.of("one", "two", "three");
List<String> dest = new ArrayList<>(List.of("a", "b", "c"));
Collections.copy(dest, src);
System.out.println(dest); // [one, two, three]
Warning:
Collections.copy()does not add elements; it only overwrites existing positions. Ifdest.size() < src.size(), you get anIndexOutOfBoundsException.
nCopies()
Returns an immutable list containing n copies of the given object:
List<String> repeated = Collections.nCopies(5, "hello");
System.out.println(repeated); // [hello, hello, hello, hello, hello]
Frequency and Disjoint
frequency()
Counts how many times an element appears in a collection:
List<String> colors = List.of("red", "blue", "red", "green", "red");
System.out.println(Collections.frequency(colors, "red")); // 3
disjoint()
Returns true if two collections have no elements in common:
List<Integer> a = List.of(1, 2, 3);
List<Integer> b = List.of(4, 5, 6);
List<Integer> c = List.of(3, 7, 8);
System.out.println(Collections.disjoint(a, b)); // true
System.out.println(Collections.disjoint(a, c)); // false
Thread-Safe Wrappers
Standard collections like ArrayList and HashMap are not thread-safe. Collections offers synchronized wrappers that add a mutex around every method:
import java.util.Collections;
import java.util.List;
import java.util.ArrayList;
List<String> safe = Collections.synchronizedList(new ArrayList<>());
safe.add("thread-safe write");
Equivalent wrappers exist for all major collection types:
| Method | Wraps |
|---|---|
synchronizedList(list) | Any List |
synchronizedSet(set) | Any Set |
synchronizedMap(map) | Any Map |
synchronizedSortedSet(set) | Any SortedSet |
synchronizedSortedMap(map) | Any SortedMap |
Tip: For high-concurrency code, prefer Concurrent Collections (
ConcurrentHashMap,CopyOnWriteArrayList) over synchronized wrappers — they offer better throughput through fine-grained locking or lock-free algorithms.
Unmodifiable Wrappers
Sometimes you want to hand a collection to external code and guarantee it won’t be changed. Unmodifiable wrappers throw UnsupportedOperationException on any mutating call:
List<String> mutable = new ArrayList<>(List.of("a", "b", "c"));
List<String> readOnly = Collections.unmodifiableList(mutable);
System.out.println(readOnly.get(0)); // a
readOnly.add("d"); // throws UnsupportedOperationException
| Method | Wraps |
|---|---|
unmodifiableList(list) | Any List |
unmodifiableSet(set) | Any Set |
unmodifiableMap(map) | Any Map |
unmodifiableSortedSet(set) | Any SortedSet |
unmodifiableSortedMap(map) | Any SortedMap |
Note: Java 9+ offers
List.of(),Set.of(), andMap.of()which create truly immutable collections without wrapping overhead. Prefer those for new code.
Singleton Collections
Need a collection with exactly one element? Collections has factory methods for that:
List<String> single = Collections.singletonList("only");
Set<Integer> oneSet = Collections.singleton(42);
Map<String,Integer> oneMap = Collections.singletonMap("key", 1);
These are immutable and extremely memory-efficient — great for passing a single argument where a collection is expected.
emptyList / emptySet / emptyMap
Similarly, typed empty collections avoid null checks and unnecessary allocations:
List<String> nothing = Collections.emptyList();
Tip: Returning
Collections.emptyList()from a method instead ofnulleliminates the dreadedNullPointerExceptionat the call site.
swap() and addAll()
swap()
Swaps two elements at specified indices:
List<String> letters = new ArrayList<>(List.of("A", "B", "C", "D"));
Collections.swap(letters, 0, 3);
System.out.println(letters); // [D, B, C, A]
addAll()
Adds multiple elements to a collection in one call — more concise than looping:
List<String> list = new ArrayList<>();
Collections.addAll(list, "X", "Y", "Z");
System.out.println(list); // [X, Y, Z]
Under the Hood
Why a Final Utility Class?
Collections is declared final with a private Collections() {} constructor — the classic utility class pattern. This prevents subclassing and instantiation, making it clear the class is purely a namespace for static methods. The JVM treats calls to these static methods as direct invocations without virtual dispatch overhead.
sort() and TimSort
Collections.sort() delegates to List.sort() which uses TimSort, a hybrid merge-sort / insertion-sort algorithm. TimSort is optimized for real-world data that often contains already-sorted runs. It runs in O(n log n) worst-case and O(n) best-case (already sorted). The sort is stable — equal elements preserve their original relative order, which matters when sorting objects with multiple fields.
Unmodifiable vs Immutable
Unmodifiable wrappers are views over the original collection. If someone still holds a reference to the backing list and mutates it, the “read-only” view reflects those changes. List.of() collections (Java 9+) store data in a private array with no backing mutable reference, so they are truly immutable.
Synchronized Wrappers Are Coarse-Grained
Every method on a synchronized wrapper acquires the same intrinsic lock (the wrapper object itself). This means add, get, size, and iteration all contend on one lock — fine for low-concurrency but a bottleneck under heavy parallel access. Iteration also requires external synchronization:
synchronized(safe) {
for (String s : safe) {
System.out.println(s);
}
}
Forgetting this synchronized block during iteration can still cause ConcurrentModificationException.
Quick Reference
| Method | What it does |
|---|---|
sort(list) | Natural-order sort |
sort(list, cmp) | Custom comparator sort |
binarySearch(list, key) | Binary search (list must be sorted) |
reverse(list) | Reverse in-place |
shuffle(list) | Random permutation |
rotate(list, dist) | Cyclic rotation |
min(col) / max(col) | Find min / max element |
fill(list, obj) | Overwrite all elements |
copy(dest, src) | Copy src → dest |
nCopies(n, obj) | Immutable list of n copies |
frequency(col, obj) | Count occurrences |
disjoint(c1, c2) | No common elements? |
swap(list, i, j) | Swap two positions |
addAll(col, …) | Bulk add varargs |
synchronizedList(list) | Thread-safe wrapper |
unmodifiableList(list) | Read-only view |
singletonList(obj) | Immutable one-element list |
emptyList() | Immutable empty list |
Related Topics
- Sorting Collections — practical guide to sorting with Comparator and Comparable together
- Comparable — make your objects naturally sortable so
Collections.sort()works out of the box - Comparator — define custom sort orders for use with
Collections.sort() - ArrayList — the list type you’ll use most often with these utilities
- Concurrent Collections — thread-safe alternatives to synchronized wrappers for high-throughput code
- Arrays Utility Class — the equivalent toolkit for plain Java arrays