Index

Collections Utility Class

Java Collections

10.1 Common Utility Methods (sort, shuffle, reverse, binarySearch)

The Collections utility class in Java provides a rich set of static methods to operate on or return collections. These methods simplify common tasks like sorting, shuffling, reversing, and searching collections, saving you from writing boilerplate code. Below, we explore some of the most commonly used methods: sort, shuffle, reverse, and binarySearch.

sort(ListT list)

The sort method arranges the elements of a list into ascending order according to their natural ordering or a provided comparator.

Example:

List<String> fruits = new ArrayList<>(Arrays.asList("Banana", "Apple", "Cherry"));
Collections.sort(fruits);
System.out.println(fruits); // Output: [Apple, Banana, Cherry]

shuffle(List? list)

The shuffle method randomly permutes the elements in the list, useful for games, random sampling, or simple randomization.

Example:

Collections.shuffle(fruits);
System.out.println(fruits); // Output: [Cherry, Apple, Banana] (order varies each run)

reverse(List? list)

This method reverses the order of elements in the specified list.

Example:

Collections.reverse(fruits);
System.out.println(fruits); // Output: [Banana, Apple, Cherry] (if original order was sorted)

binarySearch(List? extends Comparable? super T list, T key)

The binarySearch method searches for a key in a sorted list using the binary search algorithm.

Example:

int index = Collections.binarySearch(fruits, "Banana");
System.out.println(index); // Output: index of "Banana" in the sorted list, e.g., 1

If the element is not found:

int notFoundIndex = Collections.binarySearch(fruits, "Orange");
System.out.println(notFoundIndex); // Output: negative value indicating insertion point
Click to view full runnable Code

import java.util.*;

public class CollectionsDemo {
    public static void main(String[] args) {
        List<String> fruits = new ArrayList<>(Arrays.asList("Banana", "Apple", "Cherry"));

        System.out.println("Original list: " + fruits);

        // Sort the list
        Collections.sort(fruits);
        System.out.println("Sorted list: " + fruits); // [Apple, Banana, Cherry]

        // Shuffle the list
        Collections.shuffle(fruits);
        System.out.println("Shuffled list: " + fruits); // Order varies

        // Reverse the list
        Collections.reverse(fruits);
        System.out.println("Reversed list: " + fruits);

        // Sort again before binary search
        Collections.sort(fruits);
        System.out.println("Sorted list for binarySearch: " + fruits);

        // Binary search for "Banana"
        int index = Collections.binarySearch(fruits, "Banana");
        System.out.println("Index of 'Banana': " + index);

        // Binary search for a missing element
        int missing = Collections.binarySearch(fruits, "Orange");
        System.out.println("Index of 'Orange' (not found): " + missing);
    }
}

Summary Table

Method Operation In-place? Requirements Use Case
sort Sorts list Yes Elements must be Comparable or use Comparator Ordering elements for display or processing
shuffle Randomly rearranges elements Yes None Randomization tasks
reverse Reverses list order Yes None Changing order perspective
binarySearch Searches sorted list for key No List must be sorted Fast lookup in sorted data

By leveraging these utility methods, you can easily manage collection ordering, search efficiently, or introduce randomness without manually implementing complex algorithms. Just remember: for binarySearch, always keep your list sorted to guarantee correct results!

Index

10.2 Synchronized and Unmodifiable Collections

The Collections utility class offers convenient wrapper methods to create synchronized (thread-safe) and unmodifiable (read-only) views of existing collections. These wrappers help manage concurrent access or prevent accidental modification without rewriting your collections from scratch.

Synchronized Collections

Synchronized collections are essential when multiple threads access and modify the same collection concurrently. The Collections.synchronizedXXX() methods return thread-safe wrappers around your existing collections. For example:

Why use synchronized wrappers? They ensure that all access to the underlying collection is properly synchronized, preventing race conditions, data corruption, and inconsistent states.

How do they work? Each method returns a wrapper object where every mutating or reading operation is synchronized on the wrapper’s internal mutex (usually the wrapper object itself). This means only one thread at a time can perform an operation on the collection.

Important caveat: Even when using a synchronized wrapper, you must manually synchronize when iterating over the collection to avoid ConcurrentModificationException:

List<String> syncList = Collections.synchronizedList(new ArrayList<>());

// Add elements safely
syncList.add("Java");
syncList.add("Collections");

// Safe iteration requires explicit synchronization
synchronized(syncList) {
    for (String s : syncList) {
        System.out.println(s);
    }
}

Without the synchronized(syncList) block, concurrent modifications could lead to exceptions or unpredictable results.

Performance note: Synchronization adds overhead and may reduce scalability under high contention, so use it only when thread safety is needed.

Unmodifiable Collections

Unmodifiable wrappers make a collection read-only by throwing an UnsupportedOperationException if any modification is attempted. Methods include:

Why use unmodifiable collections? They provide safety guarantees by preventing accidental changes to a collection, especially when passing collections to external code or exposing internal data structures.

Example:

List<String> modifiableList = new ArrayList<>();
modifiableList.add("Java");
modifiableList.add("Collections");

List<String> readOnlyList = Collections.unmodifiableList(modifiableList);
System.out.println(readOnlyList.get(0)); // Prints "Java"

// The following line throws UnsupportedOperationException
readOnlyList.add("New Element");

Note: The unmodifiable wrapper is a view — changes to the underlying collection are reflected in the unmodifiable one. To ensure full immutability, wrap collections around immutable data or copy data before wrapping.

Summary

Wrapper Type Purpose Usage Requirement Caveats
Synchronized Thread-safe access to collections Manual synchronization during iteration Synchronization overhead
Unmodifiable Prevent modification of collections Collections still mutable underneath Throws exception on modification attempts

By using these wrappers, you gain thread safety or immutability guarantees while reusing existing collections efficiently. Just remember to handle iteration properly with synchronized wrappers and to treat unmodifiable collections as read-only views rather than fully immutable snapshots.

Index

10.3 Runnable Examples: Using utility methods effectively

import java.util.*;

public class CollectionsUtilityExamples {

    public static void main(String[] args) {
        // 1. Sorting and Searching in Lists
        List<String> fruits = new ArrayList<>(Arrays.asList("Banana", "Apple", "Orange", "Mango"));
        System.out.println("Original list: " + fruits);

        // Sort the list in natural (alphabetical) order
        Collections.sort(fruits);
        System.out.println("Sorted list: " + fruits);

        // Search for "Orange" using binarySearch (list must be sorted!)
        int index = Collections.binarySearch(fruits, "Orange");
        System.out.println("\"Orange\" found at index: " + index);

        // 2. Shuffling elements randomly
        Collections.shuffle(fruits);
        System.out.println("Shuffled list: " + fruits);

        // 3. Reversing the list order
        Collections.reverse(fruits);
        System.out.println("Reversed list: " + fruits);

        // 4. Creating synchronized collections for thread-safe access
        List<String> syncList = Collections.synchronizedList(new ArrayList<>(fruits));
        System.out.println("Synchronized list created.");

        // Demonstrate thread-safe iteration by synchronizing externally
        synchronized(syncList) {
            System.out.print("Iterating synchronized list: ");
            for (String fruit : syncList) {
                System.out.print(fruit + " ");
            }
            System.out.println();
        }

        // 5. Creating unmodifiable views to prevent modification
        List<String> unmodifiableList = Collections.unmodifiableList(syncList);
        System.out.println("Unmodifiable list view created.");

        System.out.println("Contents of unmodifiable list: " + unmodifiableList);

        // Uncommenting the following line will throw UnsupportedOperationException
        // unmodifiableList.add("Pineapple");

        /*
         * Summary:
         * - sort() rearranges elements into natural ascending order.
         * - binarySearch() requires a sorted list and returns the element's index or negative insertion point.
         * - shuffle() randomly permutes elements.
         * - reverse() flips the order of elements.
         * - synchronizedList() wraps a list to make it thread-safe; external synchronization is needed for iteration.
         * - unmodifiableList() creates a read-only view that throws exceptions on modification attempts.
         */
    }
}

Explanation:

This example illustrates how to effectively use Collections utility methods for common tasks, balancing performance and safety.

Index