Java Collection Algorithms

Java collection algorithms refer tojava.util.Collectionsa set of static methods provided in the Collections class, used to perform common operations such as sorting, searching, shuffling, filling, etc. on collections (List, Set, Map, etc.).

These algorithms are built into the JDK and are highly optimized. Developers do not need to write them from scratch; they can simply call them directly.


Collections Algorithms Overview

java.util.CollectionsCollections is a utility class; all of its methods are static, similar to the Arrays utility class for arrays.

The following table lists the most commonly used collection algorithms and their purposes:

Algorithm CategoryMethod NameDescription
Sortingsort()Sorts a List in ascending order
Shufflingshuffle()Randomly shuffles the order of elements in a List
Reversalreverse()Reverses the order of elements in a List
Binary SearchbinarySearch()Performs a binary search for a specified element in a sorted List
Fillfill()Replaces all elements in a List with a specified element
Copycopy()Copies elements from one List to another List
Maximum/Minimummax() / min()Returns the maximum or minimum element in a collection
Frequency Countfrequency()Returns the number of occurrences of a specified element in a collection
Disjoint Checkdisjoint()Determines whether two collections have any common elements
Immutable CollectionunmodifiableXxx()Returns a read-only view of a collection
Thread SafetysynchronizedXxx()Returns a thread-safe wrapper for a collection

Sorting

Collections.sort()It is the most commonly used collection algorithm, arranging the elements in a List in ascending order.

Under the hood, it uses an optimized merge sort (TimSort), with a time complexity of O(n log n) and stable performance.

The sorting process is shown in the following figure:

Input Original list 5 2 8 1 9 Arrow sort() Output Sorted list 1 2 5 8 9 Custom Sorting Custom sorting methods Natural order Comparable interface Custom comparator Comparator interface Reverse sorting Comparator.reverseOrder()

Natural Order Sorting

When the element class implementsComparablethe interface, you can directly call sort() to sort by natural order.

Example

import java.util.ArrayList;
import java.util.Collections;
import java.util.List;

public class SortExample {
    public static void main(String[] args) {
        List<String> names = new ArrayList<>();
        names.add("example");
        names.add("EXAMPLE");
        names.add("apple");
        names.add("banana");

        System.out.println("Before sorting: " + names);

        // Sort by natural order (by Unicode code point)
        Collections.sort(names);

        System.out.println("After sorting: " + names);
    }
}

Output result:

排序前: [example, EXAMPLE, apple, banana]
排序后: [EXAMPLE, apple, banana, example]

Custom Comparator Sorting

UsingComparatoryou can sort by any custom rule without modifying the element class itself.

Example: Sort by String Length

import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.List;

public class CustomSortExample {
    public static void main(String[] args) {
        List<String> words = new ArrayList<>();
        words.add("example");
        words.add("java");
        words.add("algorithm");
        words.add("C");

        System.out.println("Before sorting: " + words);

        // Sort by string length in ascending order
        Collections.sort(words, new Comparator<String>() {
            @Override
            public int compare(String s1, String s2) {
                return s1.length() - s2.length();
            }
        });

        System.out.println("After sorting by length: " + words);
    }
}

Output result:

排序前: [example, java, algorithm, C]
按长度排序后: [C, java, example, algorithm]

Java 8+ can use Lambda expressions to simplify the code:

Example: Lambda Syntax

// Sort by length using Lambda expression
Collections.sort(words, (s1, s2) -> s1.length() - s2.length());

// Or use Comparator.comparingInt
Collections.sort(words, Comparator.comparingInt(String::length));

// Reverse sort
Collections.sort(words, Comparator.reverseOrder());

// Sort by length in reverse order
Collections.sort(words, Comparator.comparingInt(String::length).reversed());

Shuffling

Collections.shuffle()Uses the Fisher-Yates algorithm to randomly shuffle the order of elements in a List.

Original list A B C D shuffle() After shuffling (random) C D A B ? ? ? Note The result is different each time it runs Fixed seed shuffle(list, new Random(42)) accepts a fixed seed, making the shuffling result reproducible.

Example

import java.util.ArrayList;
import java.util.Collections;
import java.util.List;

public class ShuffleExample {
    public static void main(String[] args) {
        List<Integer> numbers = new ArrayList<>();
        for (int i = 1; i <= 10; i++) {
            numbers.add(i);
        }

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

        // Randomly shuffle the order
        Collections.shuffle(numbers);
        System.out.println("First shuffle: " + numbers);

        Collections.shuffle(numbers);
        System.out.println("Second shuffle: " + numbers);
    }
}

Output result:

原始列表: [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
第一次混排: [7, 3, 9, 1, 5, 10, 8, 2, 4, 6]
第二次混排: [4, 10, 1, 8, 6, 3, 2, 9, 5, 7]

Binary Search

Collections.binarySearch()Uses the binary search algorithm to locate a target element in a sorted List.

The time complexity is O(log n), much better than the O(n) of linear traversal.

Important prerequisite: The List must already be sorted in ascending order; otherwise, the result is undefined.

Example

import java.util.ArrayList;
import java.util.Collections;
import java.util.List;

public class BinarySearchExample {
    public static void main(String[] args) {
        List<Integer> numbers = new ArrayList<>();
        numbers.add(10);
        numbers.add(30);
        numbers.add(20);
        numbers.add(50);
        numbers.add(40);

        // Must sort first
        Collections.sort(numbers);
        System.out.println("Sorted list: " + numbers);

        // Binary search: the return value is the index (>= 0 means found)
        int index = Collections.binarySearch(numbers, 30);
        System.out.println("Index position of 30: " + index);

        // Search for a non-existent element: returns a negative value = -(insertion point) - 1
        int notFound = Collections.binarySearch(numbers, 25);
        System.out.println("Return value for 25 (not found): " + notFound);
        // Insertion point = -(notFound + 1) = 2, so it should be inserted at index 2
    }
}

Output result:

排序后列表: [10, 20, 30, 40, 50]
30 的索引位置: 2
25 的返回值(不存在): -3

When binarySearch() cannot find an element, it returns a negative value, the formula is-(insertion point) - 1The insertion point is the index where the element would be located if it existed. The cleverness of this design is that if the element exists, a non-negative index is returned; if it does not exist, a negative value is inevitably returned, so there is no ambiguity.


Maximum and Minimum

Collections.max()andCollections.min()Returns the maximum/minimum element in a collection according to natural order or a specified comparator.

Example

import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.List;

public class MaxMinExample {
    public static void main(String[] args) {
        List<String> words = new ArrayList<>();
        words.add("example");
        words.add("algorithm");
        words.add("java");
        words.add("code");

        // Get maximum and minimum by natural order (lexicographical order)
        String maxWord = Collections.max(words);
        String minWord = Collections.min(words);
        System.out.println("Lexicographically maximum: " + maxWord);
        System.out.println("Lexicographically minimum: " + minWord);

        // Get maximum and minimum by string length
        String longest = Collections.max(words,
            Comparator.comparingInt(String::length));
        String shortest = Collections.min(words,
            Comparator.comparingInt(String::length));
        System.out.println("Longest length: " + longest);
        System.out.println("Shortest length: " + shortest);
    }
}

Output result:

字典序最大: example
字典序最小: algorithm
长度最长: algorithm
长度最短: code

Counting Frequency and Disjoint Check

Collections.frequency()Counts the number of occurrences of a specified element in a collection.

Collections.disjoint()Determines whether two collections have no common elements at all.

Example

import java.util.ArrayList;
import java.util.Collections;
import java.util.List;

public class FrequencyDisjointExample {
    public static void main(String[] args) {
        List<String> items = new ArrayList<>();
        items.add("example");
        items.add("java");
        items.add("example");
        items.add("python");
        items.add("example");

        // Count the occurrences of "example"
        int count = Collections.frequency(items, "example");
        System.out.println("\"example\""Occurrences: " + count);

        // Check whether the two collections are disjoint
        List<String> groupA = new ArrayList<>();
        groupA.add("java");
        groupA.add("python");

        List<String> groupB = new ArrayList<>();
        groupB.add("C++");
        groupB.add("go");

        boolean noCommon = Collections.disjoint(groupA, groupB);
        System.out.println("groupA and groupB are disjoint: " + noCommon);
    }
}

Output result:

"example" 出现次数: 3
groupA 与 groupB 不相交: true

Immutable Collections and Thread-Safe Wrappers

Collections provides methods to wrap ordinary collections as immutable collections or thread-safe collections.

Immutable Collections (Unmodifiable)

CallingCollections.unmodifiableList()and other methods can create a read-only view.

Any modification operation will throw UnsupportedOperationException.

Thread-Safe Collections (Synchronized)

CallingCollections.synchronizedList()and other methods can create a thread-safe wrapper.

In a multithreaded environment, individual method calls on these collections are safe, but compound operations still require external synchronization.

Example

import java.util.ArrayList;
import java.util.Collections;
import java.util.List;

public class WrapperExample {
    public static void main(String[] args) {
        List<String> original = new ArrayList<>();
        original.add("example");
        original.add("java");

        // Create an immutable view
        List<String> readOnly = Collections.unmodifiableList(original);
        System.out.println("Immutable view: " + readOnly);

        // readOnly.add("error"); // Executing this will throw an exception

        // After the original list is modified, the immutable view also reflects the changes
        original.add("python");
        System.out.println("After modifying the original: " + readOnly);

        // Create a thread-safe wrapper
        List<String> syncList = Collections.synchronizedList(
            new ArrayList<>());
        syncList.add("thread-safe");

        // Manual synchronization is required when iterating
        synchronized (syncList) {
            for (String item : syncList) {
                System.out.println(item);
            }
        }
    }
}

Output result:

不可变视图: [example, java]
原始修改后: [example, java, python]
thread-safe

An immutable view only prevents modifying the collection through that view; the underlying collection remains mutable. If you need a truly immutable collection, use Java 9+'sList.of()、Set.of()and other methods.


Common Algorithm Operations at a Glance

The following summarizes the calling methods and time complexities of common algorithms in Collections:

OperationMethod CallTime ComplexityPrerequisite
SortingCollections.sort(list)O(n log n)Elements implement Comparable
Custom sortingCollections.sort(list, comp)O(n log n)Provide a Comparator
ReversingCollections.reverse(list)O(n)None
ShufflingCollections.shuffle(list)O(n)None
Binary searchCollections.binarySearch(list, key)O(log n)The list is sorted
FillingCollections.fill(list, obj)O(n)None
CopyingCollections.copy(dest, src)O(n)dest.size() >= src.size()
MaximumCollections.max(coll)O(n)Elements implement Comparable
MinimumCollections.min(coll)O(n)Elements implement Comparable
Counting frequencyCollections.frequency(coll, obj)O(n)None
Swapping elementsCollections.swap(list, i, j)O(1)The indices are valid
RotatingCollections.rotate(list, distance)O(n)None

Notes

binarySearch() must be called on a sorted list; the result returned for an unsorted list is undefined. If the list contains duplicate elements, there is no guarantee which one will be found.

The wrapper returned by synchronizedXxx() only guarantees atomicity at the level of a single method call. Compound operations (such as if(!list.contains(x)) list.add(x)) still require an external synchronized block.

Difference between sort() and List.sort():

Since Java 8, the List interface has added the sort(Comparator) default method.

list.sort(null) sorts using natural ordering, equivalent to Collections.sort(list).

Both have the same underlying implementation, but list.sort() is more concise in syntax.

Performance comparison suggestions:

For small data volumes (fewer than 100 elements), the performance differences among various algorithms are negligible.

For large data volumes, binarySearch() is far superior to the linear search of indexOf().

When frequent lookups are needed, consider using HashSet instead of List, as the query complexity is O(1).

Other Extensions