Overview
The java.util.Collections class provides static methods that operate on or return collections. These utility methods include sorting, searching, shuffling, and synchronization wrappers.
Sorting Methods
sort() - Natural Ordering
import java.util.*;
public class SortExamples {
public static void main(String[] args) {
// Sorting List with natural ordering
List<String> names = new ArrayList<>();
names.add("John");
names.add("Alice");
names.add("Bob");
names.add("Diana");
System.out.println("Original: " + names);
Collections.sort(names);
System.out.println("Sorted (natural): " + names);
// Sorting integers
List<Integer> numbers = Arrays.asList(5, 2, 8, 1, 9);
System.out.println("Original numbers: " + numbers);
Collections.sort(numbers);
System.out.println("Sorted numbers: " + numbers);
// Sorting with custom comparator
List<String> customSort = new ArrayList<>(names);
Collections.sort(customSort, Collections.reverseOrder());
System.out.println("Reverse order: " + customSort);
// Case-insensitive sorting
List<String> mixedCase = Arrays.asList("apple", "Banana", "cherry", "Date");
Collections.sort(mixedCase, String.CASE_INSENSITIVE_ORDER);
System.out.println("Case-insensitive sort: " + mixedCase);
}
}
sort() with Custom Comparator
import java.util.*;
class Employee {
String name;
int age;
double salary;
public Employee(String name, int age, double salary) {
this.name = name;
this.age = age;
this.salary = salary;
}
@Override
public String toString() {
return name + " (" + age + ", $" + salary + ")";
}
}
public class CustomSorting {
public static void main(String[] args) {
List<Employee> employees = Arrays.asList(
new Employee("John", 30, 50000),
new Employee("Alice", 25, 60000),
new Employee("Bob", 35, 45000),
new Employee("Diana", 28, 70000)
);
// Sort by age
List<Employee> byAge = new ArrayList<>(employees);
Collections.sort(byAge, (e1, e2) -> e1.age - e2.age);
System.out.println("Sorted by age: " + byAge);
// Sort by salary (descending)
List<Employee> bySalary = new ArrayList<>(employees);
Collections.sort(bySalary, (e1, e2) -> Double.compare(e2.salary, e1.salary));
System.out.println("Sorted by salary (desc): " + bySalary);
// Sort by name
List<Employee> byName = new ArrayList<>(employees);
Collections.sort(byName, (e1, e2) -> e1.name.compareTo(e2.name));
System.out.println("Sorted by name: " + byName);
// Using Comparator.comparing (Java 8+)
List<Employee> byAgeModern = new ArrayList<>(employees);
Collections.sort(byAgeModern, Comparator.comparing(emp -> emp.age));
System.out.println("Sorted by age (modern): " + byAgeModern);
}
}
Searching Methods
binarySearch()
import java.util.*;
public class BinarySearchExamples {
public static void main(String[] args) {
// Binary search on sorted list
List<Integer> numbers = Arrays.asList(1, 3, 5, 7, 9, 11, 13, 15);
System.out.println("List: " + numbers);
int index = Collections.binarySearch(numbers, 7);
System.out.println("Index of 7: " + index);
index = Collections.binarySearch(numbers, 8);
System.out.println("Index of 8: " + index); // Negative = not found
// Binary search with custom comparator
List<String> names = Arrays.asList("Alice", "Bob", "Diana", "John");
Collections.sort(names);
index = Collections.binarySearch(names, "Diana");
System.out.println("Index of 'Diana': " + index);
// Case-insensitive binary search
List<String> mixedCase = Arrays.asList("apple", "Banana", "cherry", "Date");
Collections.sort(mixedCase, String.CASE_INSENSITIVE_ORDER);
index = Collections.binarySearch(mixedCase, "BANANA", String.CASE_INSENSITIVE_ORDER);
System.out.println("Index of 'BANANA' (case-insensitive): " + index);
// Binary search on custom objects
List<Employee> employees = Arrays.asList(
new Employee("John", 30, 50000),
new Employee("Alice", 25, 60000),
new Employee("Bob", 35, 45000)
);
Collections.sort(employees, Comparator.comparing(emp -> emp.name));
Employee searchKey = new Employee("Alice", 0, 0);
index = Collections.binarySearch(employees, searchKey,
Comparator.comparing(emp -> emp.name));
System.out.println("Index of Alice: " + index);
}
}
Shuffling and Reordering
shuffle()
import java.util.*;
public class ShuffleExamples {
public static void main(String[] args) {
List<Integer> numbers = Arrays.asList(1, 2, 3, 4, 5, 6, 7, 8, 9, 10);
System.out.println("Original: " + numbers);
// Shuffle with default randomness
Collections.shuffle(numbers);
System.out.println("Shuffled: " + numbers);
// Shuffle with specific Random object (for reproducible results)
List<Integer> reproducible = new ArrayList<>(numbers);
Random random = new Random(42); // Fixed seed
Collections.shuffle(reproducible, random);
System.out.println("Shuffled with seed: " + reproducible);
// Multiple shuffles
List<String> cards = Arrays.asList("A♠", "K♠", "Q♠", "J♠", "10♠");
System.out.println("\nCards original: " + cards);
for (int i = 1; i <= 3; i++) {
Collections.shuffle(cards);
System.out.println("Shuffle " + i + ": " + cards);
}
}
}
reverse()
import java.util.*;
public class ReverseExamples {
public static void main(String[] args) {
List<Integer> numbers = Arrays.asList(1, 2, 3, 4, 5);
System.out.println("Original: " + numbers);
Collections.reverse(numbers);
System.out.println("Reversed: " + numbers);
// Reverse a string list
List<String> words = Arrays.asList("Hello", "World", "Java", "Collections");
System.out.println("\nOriginal words: " + words);
Collections.reverse(words);
System.out.println("Reversed words: " + words);
// Reverse and then reverse back
Collections.reverse(words);
System.out.println("Back to original: " + words);
}
}
rotate()
import java.util.*;
public class RotateExamples {
public static void main(String[] args) {
List<Integer> numbers = Arrays.asList(1, 2, 3, 4, 5);
System.out.println("Original: " + numbers);
// Rotate right by 2 positions
Collections.rotate(numbers, 2);
System.out.println("Rotated right by 2: " + numbers);
// Rotate left by 3 positions (negative distance)
Collections.rotate(numbers, -3);
System.out.println("Rotated left by 3: " + numbers);
// Practical example - rotating a task list
List<String> tasks = Arrays.asList("Task1", "Task2", "Task3", "Task4", "Task5");
System.out.println("\nTask list: " + tasks);
// Move completed task to end
Collections.rotate(tasks, -1);
System.out.println("After completing first task: " + tasks);
}
}
swap()
import java.util.*;
public class SwapExamples {
public static void main(String[] args) {
List<String> names = Arrays.asList("Alice", "Bob", "Charlie", "Diana");
System.out.println("Original: " + names);
// Swap elements at positions 1 and 3
Collections.swap(names, 1, 3);
System.out.println("After swapping 1 and 3: " + names);
// Multiple swaps
List<Integer> numbers = new ArrayList<>(Arrays.asList(1, 2, 3, 4, 5));
System.out.println("\nNumbers: " + numbers);
Collections.swap(numbers, 0, numbers.size() - 1); // Swap first and last
System.out.println("After swapping first and last: " + numbers);
// Bubble sort style swapping (educational)
if (numbers.get(0) > numbers.get(1)) {
Collections.swap(numbers, 0, 1);
}
System.out.println("After conditional swap: " + numbers);
}
}
Frequency and Statistics
frequency()
import java.util.*;
public class FrequencyExamples {
public static void main(String[] args) {
List<String> words = Arrays.asList("apple", "banana", "apple", "cherry",
"banana", "apple", "date");
System.out.println("Word list: " + words);
int appleCount = Collections.frequency(words, "apple");
int bananaCount = Collections.frequency(words, "banana");
int orangeCount = Collections.frequency(words, "orange");
System.out.println("Frequency of 'apple': " + appleCount);
System.out.println("Frequency of 'banana': " + bananaCount);
System.out.println("Frequency of 'orange': " + orangeCount);
// Using with custom objects
List<Employee> employees = Arrays.asList(
new Employee("John", 30, 50000),
new Employee("Alice", 25, 60000),
new Employee("John", 35, 45000),
new Employee("Alice", 28, 70000)
);
Employee searchEmployee = new Employee("John", 0, 0);
int johnCount = Collections.frequency(employees, searchEmployee);
System.out.println("\nFrequency of John: " + johnCount); // 0 - needs equals/hashCode
// Better approach for custom objects
long aliceCount = employees.stream()
.filter(emp -> "Alice".equals(emp.name))
.count();
System.out.println("Count of Alice: " + aliceCount);
}
}
min() and max()
import java.util.*;
public class MinMaxExamples {
public static void main(String[] args) {
List<Integer> numbers = Arrays.asList(5, 2, 8, 1, 9, 3, 7);
System.out.println("Numbers: " + numbers);
Integer min = Collections.min(numbers);
Integer max = Collections.max(numbers);
System.out.println("Min: " + min);
System.out.println("Max: " + max);
// With custom comparator
List<String> words = Arrays.asList("apple", "Banana", "cherry", "Date");
String minNatural = Collections.min(words);
String minCaseInsensitive = Collections.min(words, String.CASE_INSENSITIVE_ORDER);
System.out.println("\nWords: " + words);
System.out.println("Min (natural): " + minNatural);
System.out.println("Min (case-insensitive): " + minCaseInsensitive);
// With custom objects
List<Employee> employees = Arrays.asList(
new Employee("John", 30, 50000),
new Employee("Alice", 25, 60000),
new Employee("Bob", 35, 45000)
);
Employee youngest = Collections.min(employees, Comparator.comparing(emp -> emp.age));
Employee highestPaid = Collections.max(employees, Comparator.comparing(emp -> emp.salary));
System.out.println("\nYoungest employee: " + youngest);
System.out.println("Highest paid employee: " + highestPaid);
}
}
Synchronization Wrappers
Synchronized Collections
import java.util.*;
public class SynchronizedCollections {
public static void main(String[] args) throws InterruptedException {
// Create synchronized wrappers
List<String> syncList = Collections.synchronizedList(new ArrayList<>());
Set<Integer> syncSet = Collections.synchronizedSet(new HashSet<>());
Map<String, Integer> syncMap = Collections.synchronizedMap(new HashMap<>());
// Thread-safe operations
Runnable listWriter = () -> {
for (int i = 0; i < 1000; i++) {
syncList.add("Item-" + i);
}
};
Runnable setWriter = () -> {
for (int i = 0; i < 1000; i++) {
syncSet.add(i);
}
};
// Start multiple threads
Thread t1 = new Thread(listWriter);
Thread t2 = new Thread(listWriter);
Thread t3 = new Thread(setWriter);
Thread t4 = new Thread(setWriter);
t1.start();
t2.start();
t3.start();
t4.start();
t1.join();
t2.join();
t3.join();
t4.join();
System.out.println("SyncList size: " + syncList.size());
System.out.println("SyncSet size: " + syncSet.size());
// Important: When iterating, you must synchronize manually
synchronized(syncList) {
Iterator<String> it = syncList.iterator();
while (it.hasNext()) {
// Safe iteration
String item = it.next();
}
}
}
}
Unmodifiable Collections
import java.util.*;
public class UnmodifiableCollections {
public static void main(String[] args) {
List<String> originalList = new ArrayList<>(Arrays.asList("A", "B", "C"));
Set<Integer> originalSet = new HashSet<>(Arrays.asList(1, 2, 3));
Map<String, Integer> originalMap = new HashMap<>();
originalMap.put("One", 1);
originalMap.put("Two", 2);
// Create unmodifiable views
List<String> unmodifiableList = Collections.unmodifiableList(originalList);
Set<Integer> unmodifiableSet = Collections.unmodifiableSet(originalSet);
Map<String, Integer> unmodifiableMap = Collections.unmodifiableMap(originalMap);
System.out.println("Unmodifiable list: " + unmodifiableList);
System.out.println("Unmodifiable set: " + unmodifiableSet);
System.out.println("Unmodifiable map: " + unmodifiableMap);
// These operations will throw UnsupportedOperationException
try {
unmodifiableList.add("D"); // Throws exception
} catch (UnsupportedOperationException e) {
System.out.println("Cannot modify unmodifiable list");
}
try {
unmodifiableSet.remove(1); // Throws exception
} catch (UnsupportedOperationException e) {
System.out.println("Cannot modify unmodifiable set");
}
// But original collections can still be modified
originalList.add("D");
originalSet.add(4);
originalMap.put("Three", 3);
System.out.println("After modifying originals:");
System.out.println("Unmodifiable list: " + unmodifiableList);
System.out.println("Unmodifiable set: " + unmodifiableSet);
System.out.println("Unmodifiable map: " + unmodifiableMap);
}
}
Fill and Copy Operations
fill()
import java.util.*;
public class FillExamples {
public static void main(String[] args) {
// Fill a list with same value
List<String> list = new ArrayList<>(Arrays.asList("A", "B", "C", "D", "E"));
System.out.println("Original: " + list);
Collections.fill(list, "X");
System.out.println("After fill with 'X': " + list);
// Fill an array
Integer[] numbers = new Integer[5];
Arrays.fill(numbers, 42);
System.out.println("Filled array: " + Arrays.toString(numbers));
// Practical example - resetting values
List<Boolean> flags = new ArrayList<>(Collections.nCopies(10, false));
System.out.println("Initial flags: " + flags);
Collections.fill(flags, true);
System.out.println("All flags true: " + flags);
}
}
copy()
import java.util.*;
public class CopyExamples {
public static void main(String[] args) {
// Copy from source to destination
List<String> source = Arrays.asList("A", "B", "C", "D", "E");
List<String> dest = new ArrayList<>(Arrays.asList("1", "2", "3", "4", "5"));
System.out.println("Source: " + source);
System.out.println("Destination before copy: " + dest);
Collections.copy(dest, source);
System.out.println("Destination after copy: " + dest);
// What happens with different sizes
List<String> smallDest = new ArrayList<>(Arrays.asList("1", "2", "3"));
try {
Collections.copy(smallDest, source); // Throws IndexOutOfBoundsException
} catch (IndexOutOfBoundsException e) {
System.out.println("Cannot copy - destination too small");
}
List<String> largeDest = new ArrayList<>(Arrays.asList("1", "2", "3", "4", "5", "6", "7"));
System.out.println("\nLarge destination before: " + largeDest);
Collections.copy(largeDest, source);
System.out.println("Large destination after: " + largeDest);
}
}
nCopies() and singleton Methods
nCopies()
import java.util.*;
public class NCopiesExamples {
public static void main(String[] args) {
// Create immutable list with n copies
List<String> copies = Collections.nCopies(5, "Hello");
System.out.println("nCopies result: " + copies);
// The list is immutable
try {
copies.add("World"); // Throws UnsupportedOperationException
} catch (UnsupportedOperationException e) {
System.out.println("nCopies list is immutable");
}
// Practical uses
List<String> initialList = new ArrayList<>(Collections.nCopies(10, ""));
System.out.println("Initialized list: " + initialList);
// Initialize with default values
List<Integer> defaultNumbers = new ArrayList<>(Collections.nCopies(8, 0));
System.out.println("Default numbers: " + defaultNumbers);
}
}
singleton Methods
import java.util.*;
public class SingletonExamples {
public static void main(String[] args) {
// Create singleton collections
Set<String> singletonSet = Collections.singleton("Unique");
List<Integer> singletonList = Collections.singletonList(42);
Map<String, String> singletonMap = Collections.singletonMap("key", "value");
System.out.println("Singleton set: " + singletonSet);
System.out.println("Singleton list: " + singletonList);
System.out.println("Singleton map: " + singletonMap);
// All are immutable
try {
singletonSet.add("Another"); // Throws exception
} catch (UnsupportedOperationException e) {
System.out.println("Singleton collections are immutable");
}
// Practical use - removing single element
List<String> names = new ArrayList<>(Arrays.asList("John", "Alice", "Bob", "Alice"));
System.out.println("Before remove: " + names);
names.removeAll(Collections.singleton("Alice"));
System.out.println("After removing all 'Alice': " + names);
}
}
Checked Collections (Type Safety)
checkedCollection(), checkedList(), checkedSet(), checkedMap()
import java.util.*;
public class CheckedCollections {
public static void main(String[] args) {
// Create type-safe collections
List<String> safeList = Collections.checkedList(new ArrayList<>(), String.class);
Set<Integer> safeSet = Collections.checkedSet(new HashSet<>(), Integer.class);
Map<String, Integer> safeMap = Collections.checkedMap(new HashMap<>(),
String.class, Integer.class);
// These work fine
safeList.add("Hello");
safeSet.add(42);
safeMap.put("Count", 100);
System.out.println("Safe collections: " + safeList + ", " + safeSet + ", " + safeMap);
// This would throw ClassCastException at the point of insertion
try {
// safeList.add(42); // Compile error if types are known
} catch (Exception e) {
System.out.println("Type violation caught: " + e);
}
// More realistic example with raw types
List rawList = new ArrayList();
List<String> checkedList = Collections.checkedList(rawList, String.class);
rawList.add("Valid"); // OK
try {
rawList.add(42); // Throws ClassCastException immediately
} catch (ClassCastException e) {
System.out.println("Caught type violation: " + e.getMessage());
}
}
}
Empty Collections
emptyList(), emptySet(), emptyMap()
import java.util.*;
public class EmptyCollections {
public static void main(String[] args) {
// Get immutable empty collections
List<String> emptyList = Collections.emptyList();
Set<Integer> emptySet = Collections.emptySet();
Map<String, Object> emptyMap = Collections.emptyMap();
System.out.println("Empty list: " + emptyList);
System.out.println("Empty set: " + emptySet);
System.out.println("Empty map: " + emptyMap);
// All are immutable
try {
emptyList.add("Something"); // Throws exception
} catch (UnsupportedOperationException e) {
System.out.println("Empty collections are immutable");
}
// Practical use - return empty collections instead of null
List<String> searchResults = searchDatabase("query");
System.out.println("Search results: " + searchResults);
}
// Good practice: return empty collection instead of null
public static List<String> searchDatabase(String query) {
if (query == null || query.trim().isEmpty()) {
return Collections.emptyList(); // Better than returning null
}
// Actual search logic
return Arrays.asList("Result1", "Result2");
}
}
Disjoint and AddAll
disjoint()
import java.util.*;
public class DisjointExamples {
public static void main(String[] args) {
List<Integer> list1 = Arrays.asList(1, 2, 3, 4, 5);
List<Integer> list2 = Arrays.asList(6, 7, 8, 9, 10);
List<Integer> list3 = Arrays.asList(5, 6, 7, 8, 9);
System.out.println("List1: " + list1);
System.out.println("List2: " + list2);
System.out.println("List3: " + list3);
boolean disjoint1 = Collections.disjoint(list1, list2);
boolean disjoint2 = Collections.disjoint(list1, list3);
System.out.println("List1 and List2 are disjoint: " + disjoint1);
System.out.println("List1 and List3 are disjoint: " + disjoint2);
// Practical use - checking for common elements
List<String> userRoles = Arrays.asList("admin", "user", "editor");
List<String> requiredRoles = Arrays.asList("admin", "superuser");
if (!Collections.disjoint(userRoles, requiredRoles)) {
System.out.println("User has at least one required role");
} else {
System.out.println("User has none of the required roles");
}
}
}
addAll()
import java.util.*;
public class AddAllExamples {
public static void main(String[] args) {
List<String> list = new ArrayList<>();
// Add multiple elements at once
boolean changed = Collections.addAll(list, "A", "B", "C", "D", "E");
System.out.println("Collection changed: " + changed);
System.out.println("List after addAll: " + list);
// Add from array
String[] moreElements = {"F", "G", "H"};
Collections.addAll(list, moreElements);
System.out.println("After adding array: " + list);
// Compared to List.addAll()
List<String> anotherList = new ArrayList<>();
anotherList.addAll(Arrays.asList("X", "Y", "Z"));
System.out.println("Using List.addAll(): " + anotherList);
// Collections.addAll is more flexible
Set<Integer> numberSet = new HashSet<>();
Collections.addAll(numberSet, 1, 2, 3, 2, 1); // Duplicates will be removed
System.out.println("Number set: " + numberSet);
}
}
Replacement Operations
replaceAll()
import java.util.*;
public class ReplaceAllExamples {
public static void main(String[] args) {
List<String> words = new ArrayList<>(
Arrays.asList("apple", "banana", "cherry", "apple", "date")
);
System.out.println("Original: " + words);
// Replace all occurrences
boolean replaced = Collections.replaceAll(words, "apple", "orange");
System.out.println("Replacements made: " + replaced);
System.out.println("After replaceAll: " + words);
// No replacements if oldValue not found
replaced = Collections.replaceAll(words, "grape", "pear");
System.out.println("Replacements made: " + replaced);
System.out.println("After trying to replace grape: " + words);
// Case-sensitive replacement
List<String> mixedCase = Arrays.asList("Apple", "apple", "APPLE", "banana");
System.out.println("\nMixed case: " + mixedCase);
Collections.replaceAll(mixedCase, "apple", "orange");
System.out.println("After case-sensitive replace: " + mixedCase);
}
}
Performance Tips and Best Practices
import java.util.*;
public class CollectionsBestPractices {
public static void main(String[] args) {
// 1. Use appropriate collections
List<String> arrayList = new ArrayList<>(); // Random access
List<String> linkedList = new LinkedList<>(); // Frequent insertions/deletions
// 2. Prefer interface types
List<Integer> numbers = new ArrayList<>();
Set<String> uniqueNames = new HashSet<>();
Map<String, Integer> wordCount = new HashMap<>();
// 3. Use utility methods for common operations
List<String> list = Arrays.asList("A", "B", "C");
Collections.sort(list);
Collections.reverse(list);
Collections.shuffle(list);
// 4. Use unmodifiable collections for constants
final List<String> CONSTANTS = Collections.unmodifiableList(
Arrays.asList("CONST1", "CONST2", "CONST3")
);
// 5. Use empty collections instead of null
public List<String> getResults() {
return results.isEmpty() ? Collections.emptyList() : new ArrayList<>(results);
}
// 6. Use synchronized wrappers for thread safety
List<String> threadSafeList = Collections.synchronizedList(new ArrayList<>());
// 7. Use checked collections for runtime type safety
List<String> typeSafeList = Collections.checkedList(new ArrayList<>(), String.class);
// 8. Use singleton collections for single elements
set.removeAll(Collections.singleton("remove-me"));
}
}
The Collections utility class is one of the most powerful and frequently used classes in Java, providing essential algorithms and wrappers that make working with collections much easier and safer.