The Collections class ships a handful of fundamental algorithms that operate on List (mostly). Most are O(n) in-place mutations; sort and binarySearch are O(n log n) / O(log n). Worth knowing because they're often the shortest, clearest way to express common operations.
sort
List<Integer> xs = new ArrayList<>(List.of(3, 1, 4, 1, 5, 9, 2, 6));
Collections.sort(xs); // natural order
Collections.sort(xs, Comparator.reverseOrder()); // custom comparator
Uses TimSort under the hood — adaptive merge sort that's O(n log n) worst case and O(n) on already-sorted or nearly-sorted input. Stable. In Java 8+ you can also do list.sort(comparator) directly on the list.
binarySearch
List<Integer> sorted = List.of(1, 3, 5, 7, 9);
int idx = Collections.binarySearch(sorted, 5); // returns 2
int miss = Collections.binarySearch(sorted, 4); // returns -(insertion point) - 1
// here: -3 (would insert at idx 2)
O(log n) on RandomAccess lists (ArrayList), O(n) on linked lists due to traversal. Input must be sorted by the same comparator (or natural order) you pass — undefined behavior otherwise. The negative return on miss is -(insertionPoint) - 1 so you can compute insertion point as -result - 1.
reverse
List<String> xs = new ArrayList<>(List.of("a", "b", "c", "d"));
Collections.reverse(xs); // [d, c, b, a]
In-place, O(n). For an immutable reversed view, use Sequenced Collections (Java 21+): list.reversed().
shuffle
Collections.shuffle(xs); // uses ThreadLocalRandom
Collections.shuffle(xs, new Random(42)); // reproducible with seed
Fisher-Yates, O(n). For tests, always pass a seeded Random so failures are reproducible.
rotate
List<Integer> xs = new ArrayList<>(List.of(1, 2, 3, 4, 5));
Collections.rotate(xs, 2); // [4, 5, 1, 2, 3] — shifts right by 2
Collections.rotate(xs, -1); // shifts left by 1
In-place, O(n). Useful for circular buffers and "everyone passes their cards left" simulations.
frequency
List<String> words = List.of("a", "b", "a", "c", "a", "b");
int count = Collections.frequency(words, "a"); // 3
O(n) linear count. Equivalent to streams (stream.filter(x::equals).count()) but more concise. Uses equals, handles null.
disjoint
boolean noOverlap = Collections.disjoint(
List.of(1, 2, 3),
Set.of(4, 5, 6)
); // true
true iff the two collections share no element. Smart enough to iterate the smaller collection and look up in the larger — pass a Set as one argument for O(n) total instead of O(n*m).
Quick reference
| Operation | Best | Average | Worst | Note |
|---|---|---|---|---|
| sort | O(n) | O(n log n) | O(n log n) | TimSort, stable |
| binarySearch (RandomAccess) | O(1) | O(log n) | O(log n) | requires sorted |
| reverse / rotate | O(n) | O(n) | O(n) | in-place |
| shuffle | O(n) | O(n) | O(n) | Fisher-Yates |
| frequency | O(n) | O(n) | O(n) | linear scan |
| disjoint | O(1) | O(n) | O(n*m) | depends on input types |