Collections Class in Java: Methods and Examples
Call Collections.binarySearch(list, "red") on the list [red, blue, green] and the method returns -4, meaning “not found”, although "red" sits at index 0. No exception is thrown: binarySearch() simply assumes the list is sorted. Almost every method of the Collections class has hidden conditions like this, and this lesson collects them together with real program output.
The java.util.Collections class is the utility class of the Java Collections Framework. It contains only static methods for working with collections: sorting, binary search, shuffling, reversing, finding the maximum and minimum, counting elements, and creating unmodifiable, synchronized and type-checked wrappers. Its constructor is private, so you cannot create a Collections object. Most methods take a List, some accept any Collection. Do not confuse it with the Collection interface — the difference is explained in Java Collections Framework.
Main methods of the Collections class
The main methods of the Collections class fall into three groups: algorithms over a list (sort(), binarySearch(), reverse(), shuffle(), swap(), fill(), copy(), rotate()), calculations over any collection (max(), min(), frequency(), disjoint()), and factories of lists and wrappers (nCopies(), emptyList(), unmodifiableList(), synchronizedList()). All of them are static and are called through the class name.
| Method | What it does | Edge case / error |
|---|---|---|
sort(list), sort(list, cmp) | Sorts the list in place, stably, in O(n log n) | A list from List.of() — UnsupportedOperationException; a null element with natural ordering — NullPointerException |
binarySearch(list, key), binarySearch(list, key, cmp) | Returns the index of the element or -(insertion point) - 1 | The list is not sorted (or sorted with another comparator) — the result is undefined |
reverse(list) | Reverses the order of elements | An empty list or a list of one element stays unchanged |
shuffle(list), shuffle(list, rnd) | Randomly permutes the elements | With new Random(seed) the order is the same on every run |
swap(list, i, j) | Swaps two elements | An index outside [0, size()) — IndexOutOfBoundsException |
fill(list, obj) | Replaces all elements with one value | Does not change the size: an empty list stays empty |
nCopies(n, obj) | Immutable list of n equal elements | n < 0 — IllegalArgumentException; add() — UnsupportedOperationException |
max(coll), min(coll), versions with cmp | The largest / smallest element | An empty collection — NoSuchElementException |
copy(dest, src) | Copies the elements of src over the beginning of dest | dest.size() < src.size() — IndexOutOfBoundsException: Source does not fit in dest |
rotate(list, distance) | Cyclically shifts the elements by distance positions | A negative value shifts left; distance is taken modulo the size |
frequency(coll, obj) | Counts how many times an element occurs in the collection | Compares with equals(); obj = null is allowed |
disjoint(c1, c2) | true if the collections have no common elements | Two empty collections — true |
addAll(coll, e1, e2, ...) | Adds the listed elements, returns true if the collection changed | A list from Arrays.asList() — UnsupportedOperationException |
reverseOrder(), reverseOrder(cmp) | A comparator that reverses the natural order or the given comparator | reverseOrder(null) is the same as reverseOrder(); equivalent to Comparator.reverseOrder() |
emptyList(), singletonList(obj) | Immutable empty list / list of one element | add() — UnsupportedOperationException |
unmodifiableList(list) | Read-only view | Changes of the original list are visible through the wrapper |
synchronizedList(list) | Wrapper that synchronizes every method | Iteration must be wrapped manually in synchronized (list) { ... } |
checkedList(list, type) | Checks the type of every added element | A wrong type — ClassCastException at the moment of insertion |
Most of these methods have counterparts for other collection types: emptySet(), emptyMap(), unmodifiableSet(), synchronizedMap(), checkedMap() and so on. The examples below use Arrays.asList() — a short way to get a fixed-size list that supports set(), and therefore every algorithm that rearranges elements.
Which lists can be passed to Collections
sort(), reverse(), shuffle(), swap(), fill() and rotate() change a list through set(), so they work with ArrayList, LinkedList and Arrays.asList(). Lists from List.of(), nCopies() and unmodifiableList() answer any rearrangement with UnsupportedOperationException — make a copy first: new ArrayList<>(List.of(...)). Note that Arrays.asList() is a view of the original array: writing to the list writes to the array.
Collections.sort() method
The Collections.sort(list) method sorts a list in place in the natural order (the elements must implement Comparable), and Collections.sort(list, comparator) sorts it in the order defined by the comparator. The sort is stable: equal elements keep their original relative order. To sort in descending order, pass Collections.reverseOrder().
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
public class SortCollections {
public static void main(String[] args) {
List<String> list = Arrays.asList("red", "blue", "green");
System.out.println("Before sorting: " + list);
Collections.sort(list);
System.out.println("After sorting: " + list);
Collections.sort(list, Collections.reverseOrder());
System.out.println("After reverse sorting: " + list);
}
} Output:
Before sorting: [red, blue, green]
After sorting: [blue, green, red]
After reverse sorting: [red, green, blue] Strings are compared by UTF-16 character codes, not in dictionary order, so every uppercase letter comes before every lowercase one — a surprising result of this rule is shown in Collections.max() and min(), and the full explanation with String.CASE_INSENSITIVE_ORDER and Collator is in Comparable and Comparator. Since Java 8, Collections.sort(list) simply calls list.sort(null); the difference between the two forms is covered in Sorting a list: list.sort(). To build a comparator from several fields, see Comparator.comparing, thenComparing, reversed. Arrays are sorted with a different class — see Arrays.sort() in Java.
Collections.binarySearch() method
The Collections.binarySearch(list, key) method looks for an element in a sorted list using binary search and returns its index. If the element is absent, the result is negative: -(insertion point) - 1, where the insertion point is the position the element would take in the sorted list. For random-access lists such as ArrayList the search takes O(log n).
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
public class BinarySearchExample {
public static void main(String[] args) {
List<String> list = Arrays.asList("red", "blue", "green");
Collections.sort(list);
System.out.println(list);
System.out.println(Collections.binarySearch(list, "green"));
System.out.println(Collections.binarySearch(list, "yellow"));
}
} Output:
[blue, green, red]
1
-4 “yellow” would sit after “red”, at position 3, so the method returned -(3) - 1 = -4. To get the insertion point back from a negative result, use -result - 1.
What does this code print if you forget to sort the list?
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
public class UnsortedBinarySearch {
public static void main(String[] args) {
List<String> list = Arrays.asList("red", "blue", "green");
System.out.println(list.indexOf("red"));
System.out.println(Collections.binarySearch(list, "red"));
}
} Output:
0
-4 Binary search compared “red” with the middle element “blue”, decided that it must be to the right, compared it with “green” and ran off the end of the list — it never looked at position 0. Java does not check whether the list is sorted, so the mistake shows up only as a wrong result. The same rule applies to comparators: if the list is sorted with Collections.reverseOrder(), search with the same comparator — Collections.binarySearch(list, key, Collections.reverseOrder()).
reverse(), shuffle() and swap() methods
The Collections.reverse(list) method reverses the order of the list elements, Collections.shuffle(list) rearranges them randomly, and Collections.swap(list, i, j) swaps the elements at indexes i and j. All three modify the list itself and return nothing.
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
import java.util.Random;
public class ReverseShuffleExample {
public static void main(String[] args) {
List<String> list = Arrays.asList("red", "blue", "green", "black");
System.out.println("Original list: " + list);
Collections.reverse(list);
System.out.println("After reverse(): " + list);
Collections.swap(list, 0, 3);
System.out.println("After swap(0, 3): " + list);
Collections.shuffle(list);
System.out.println("After shuffle(): " + list);
List<String> deck = Arrays.asList("red", "blue", "green", "black");
Collections.shuffle(deck, new Random(42));
System.out.println("shuffle with Random(42): " + deck);
}
} Output (the shuffle() line is different on every run, the last line is always the same):
Original list: [red, blue, green, black]
After reverse(): [black, green, blue, red]
After swap(0, 3): [red, green, blue, black]
After shuffle(): [green, black, red, blue]
shuffle with Random(42): [black, blue, red, green] By default shuffle() uses an internal random number generator. If you need the shuffle to repeat from run to run (tests, reproducing a bug, the same deck in a game), pass your own generator with a fixed seed: Collections.shuffle(list, new Random(42)) — the same starting list always gives the same order. Calling Collections.swap(list, 0, 4) on a list of four elements throws IndexOutOfBoundsException.
fill() and nCopies() methods
The Collections.fill(list, obj) method replaces every element of a list with obj but never adds new elements. To create a list of n equal values use Collections.nCopies(n, obj): it returns an immutable list that is usually copied into an ArrayList right away.
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
public class CollectionsFillExample {
public static void main(String[] args) {
List<String> colors = Arrays.asList("red", "blue", "green");
Collections.fill(colors, "black");
System.out.println(colors);
List<String> empty = new ArrayList<>();
Collections.fill(empty, "black");
System.out.println(empty);
List<String> filled = new ArrayList<>(Collections.nCopies(3, "black"));
filled.add("white");
System.out.println(filled);
}
} Output:
[black, black, black]
[]
[black, black, black, white] The empty list is still empty after fill(): there is nothing to replace. A list from nCopies() cannot be changed without copying it — add() throws UnsupportedOperationException, and nCopies(-1, "x") throws IllegalArgumentException. All n positions refer to the same object, so for mutable objects (for example nCopies(3, new StringBuilder())) a change to one element is visible in all of them.
Collections.max() and min() methods
The Collections.max(collection) and Collections.min(collection) methods return the largest and the smallest element of any collection in the natural order, and the overloads with a comparator do it by the given rule. The collection is traversed once in O(n), so there is no need to sort it first. For an empty collection both methods throw NoSuchElementException.
import java.util.Arrays;
import java.util.Collections;
import java.util.Comparator;
import java.util.List;
public class CollectionsMinMaxExample {
public static void main(String[] args) {
List<Integer> numbers = Arrays.asList(2, 2, 5, 8, 9);
System.out.println(Collections.max(numbers));
System.out.println(Collections.min(numbers));
List<String> words = Arrays.asList("cat", "dog", "Zebra");
System.out.println(Collections.max(words, Comparator.comparingInt(String::length)));
System.out.println(Collections.max(words));
System.out.println(Collections.min(words));
}
} Output:
9
2
Zebra
dog
Zebra The last two lines surprise many people: “Zebra” is the smallest word and “dog” the largest, because the code of the uppercase letter Z (90) is smaller than the code of c (99). The comparator by length gives the expected “Zebra”. A call such as Collections.max(new ArrayList<Integer>()) ends with java.util.NoSuchElementException — if the collection may be empty, check isEmpty() first or use stream().max(cmp), which returns an Optional.
Collections.copy() method
The Collections.copy(dest, src) method writes the elements of the src list over the first positions of the dest list; the remaining elements of dest are not touched. The size of dest must be at least the size of src, otherwise the method throws IndexOutOfBoundsException. The capacity of an ArrayList does not count here, only size() does.
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
public class CollectionsCopyExample {
public static void main(String[] args) {
List<Integer> src = Arrays.asList(1, 2, 3);
List<Integer> dest = Arrays.asList(4, 5, 6, 7);
Collections.copy(dest, src);
System.out.println(dest);
List<Integer> empty = new ArrayList<>(10);
Collections.copy(empty, src);
}
} Output:
[1, 2, 3, 7]
Exception in thread "main" java.lang.IndexOutOfBoundsException: Source does not fit in dest The list new ArrayList<>(10) only looks roomy: its size() is 0. If you need an independent copy of a list rather than an overwrite of an existing one, write new ArrayList<>(src) or the immutable List.copyOf(src) (Java 10).
Collections.rotate() method
The Collections.rotate(list, distance) method cyclically shifts the elements of a list: the element at index i moves to position (i + distance) % size. A positive distance shifts the elements to the right, a negative one to the left, and elements pushed off the end reappear at the beginning.
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
public class CollectionsRotateExample {
public static void main(String[] args) {
List<String> list = new ArrayList<>(List.of("a", "b", "c", "d", "e"));
System.out.println(list);
Collections.rotate(list, 2);
System.out.println(list);
Collections.rotate(list, -1);
System.out.println(list);
List<String> letters = new ArrayList<>(List.of("a", "b", "c", "d", "e"));
Collections.rotate(letters.subList(1, 4), -1);
System.out.println(letters);
}
} Output:
[a, b, c, d, e]
[d, e, a, b, c]
[e, a, b, c, d]
[a, c, d, b, e] A shift larger than the list size is not an error: rotate(list, 7) on five elements gives the same result as rotate(list, 2). The last example is a trick from the JDK documentation: rotate() on a sublist moves the element "b" from position 1 to position 3 and leaves the rest of the list alone.
frequency() and disjoint() methods
The Collections.frequency(collection, obj) method returns how many times an element occurs in a collection, comparing elements with equals(). The Collections.disjoint(c1, c2) method returns true if two collections have no common element. Both work with any Collection, not only with lists.
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
public class CollectionsFrequencyExample {
public static void main(String[] args) {
List<String> colors = Arrays.asList("red", "blue", "red", null);
System.out.println(Collections.frequency(colors, "red"));
System.out.println(Collections.frequency(colors, "Red"));
System.out.println(Collections.frequency(colors, null));
List<String> warm = List.of("red", "orange");
List<String> cold = List.of("blue", "cyan");
System.out.println(Collections.disjoint(colors, warm));
System.out.println(Collections.disjoint(warm, cold));
}
} Output:
2
0
1
false
true “Red” with a capital letter is not found: equals() is case-sensitive. For your own classes frequency() counts correctly only if they override the equals() method. The method walks through the whole collection, so counting the frequency of every element in a loop takes O(n²) — for large data a HashMap with counters is a better choice.
Creating lists: addAll(), emptyList(), singletonList()
The Collections.addAll(collection, e1, e2, ...) method adds the listed elements to an existing collection and returns true if the collection changed. The emptyList() and singletonList(obj) methods return immutable lists of zero and one element — they are used as return values so that no extra ArrayList is created.
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
public class CollectionsCreateExample {
public static void main(String[] args) {
List<String> list = new ArrayList<>();
System.out.println(Collections.addAll(list, "a", "b", "c"));
System.out.println(list);
List<String> none = Collections.emptyList();
List<String> one = Collections.singletonList("a");
System.out.println(none + " " + one);
one.add("b");
}
} Output:
true
[a, b, c]
[] [a]
Exception in thread "main" java.lang.UnsupportedOperationException For a Set, addAll() returns false if all the elements were already there. Since Java 9, List.of() and List.of(obj) are often written instead of emptyList() and singletonList(); the difference is that singletonList(null) is allowed, while List.of(null) throws NullPointerException.
Wrappers: checkedList(), unmodifiableList(), synchronizedList()
The wrappers of the Collections class return a new view of the same list with extra behavior: checkedList() checks the type of every added element, unmodifiableList() forbids changes, and synchronizedList() synchronizes the methods. The data is not copied — the wrapper works with the original list, so changes made around it are visible through the wrapper.
The checked wrapper (checkedList(), checkedCollection(), checkedMap()) is needed where generics do not protect you: code with raw types or an old library can put a number into a List<String>, and the error shows up far from the place of insertion. The wrapper throws immediately:
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
public class CheckedListExample {
public static void main(String[] args) {
List<String> names = new ArrayList<>(List.of("one", "two"));
List<String> checked = Collections.checkedList(names, String.class);
addRaw(names, 10); // gets into the list unchecked
System.out.println(names);
addRaw(checked, 20); // ClassCastException
}
// imitates old code without generics
@SuppressWarnings({"rawtypes", "unchecked"})
static void addRaw(List list, Object value) {
list.add(value);
}
} Output:
[one, two, 10]
Exception in thread "main" java.lang.ClassCastException: Attempt to insert class java.lang.Integer element into collection with element type class java.lang.String How unmodifiableList() differs from List.of() and List.copyOf() is compared in the table of the Immutable collections section, and when to take CopyOnWriteArrayList or ConcurrentHashMap instead of synchronizedList() and synchronizedMap() is covered in Thread-safe collections in Java.
New in Java 21
Together with the SequencedCollection, SequencedSet and SequencedMap interfaces (JEP 431), the Collections class got the wrappers unmodifiableSequencedCollection(), unmodifiableSequencedSet() and unmodifiableSequencedMap(). They keep the getFirst(), getLast() and reversed() methods that the ordinary unmodifiableCollection() does not have. Java 21 also added the overload shuffle(list, RandomGenerator), so any generator from java.util.random can be passed, not only Random. Java 17 has none of these methods.
Frequently Asked Questions
How do I find the most frequent element of a list with Collections?
Combine max() and frequency(): Collections.max(list, Comparator.comparingInt(x -> Collections.frequency(list, x))). For [a, b, a, c, b, a] the result is a; when frequencies are equal, the element that comes first is returned. This is O(n²), so for large lists count the frequencies in one pass with a HashMap. See frequency() and disjoint() methods for how frequency() works.
How do I reverse a list without changing the original?
Collections.reverse() changes the list itself, so make a copy first: List<String> copy = new ArrayList<>(list); Collections.reverse(copy);. Java 21 has list.reversed() — it returns a reversed view without copying, but changes made through it go to the original list.
Why use Collections.emptyList() if I can return new ArrayList<>()?
emptyList() returns one shared immutable object, so it does not allocate memory on each call and protects against accidental changes: the calling code gets UnsupportedOperationException on add(). If the caller needs to fill the result, return new ArrayList<>().
Do Collections methods work with arrays and maps?
Not with arrays: they have a similar class, Arrays (Arrays.sort(), Arrays.binarySearch(), Arrays.fill()), and an array of objects can be wrapped into a list with Arrays.asList(). Algorithms such as sort() and shuffle() are not applicable to a Map, but maps have wrappers: unmodifiableMap(), synchronizedMap(), checkedMap(), emptyMap(), singletonMap().
Comments