Коллекции ·
‹ Предыдущий Следующий ›
⏱ 5 минут чтения Обновлено: 2026-10-25

Класс Collections в Java: методы и примеры

Вызовите Collections.binarySearch(list, "зеленый") на списке [красный, синий, зеленый] — метод вернёт -1, то есть «не найдено», хотя элемент лежит в позиции 2. Исключения не будет: binarySearch() рассчитан только на отсортированный список. Такие неявные условия есть почти у каждого метода класса Collections, и ниже они собраны вместе с выводом программ.

Класс java.util.Collections — служебный (utility) класс Collection Framework, который состоит только из статических методов для работы с коллекциями: сортировки, бинарного поиска, перемешивания, переворота, поиска максимума и минимума, подсчёта элементов, а также создания неизменяемых, синхронизированных и проверяемых обёрток. Конструктор класса закрытый, создать объект Collections нельзя. Большинство методов принимает List, часть — любую Collection. Не путайте его с интерфейсом Collection — разница объяснена в уроке Коллекции в Java.

Основные методы класса Collections

Основные методы класса Collections делятся на три группы: алгоритмы над списком (sort(), binarySearch(), reverse(), shuffle(), swap(), fill(), copy(), rotate()), вычисления над любой коллекцией (max(), min(), frequency(), disjoint()) и фабрики списков и обёрток (nCopies(), emptyList(), unmodifiableList(), synchronizedList()). Все методы статические и вызываются через имя класса.

Метод Что делает Граничный случай / ошибка
sort(list), sort(list, cmp) Сортирует список на месте, стабильно, за O(n log n) Список из List.of() — UnsupportedOperationException; null-элемент при естественном порядке — NullPointerException
binarySearch(list, key), binarySearch(list, key, cmp) Возвращает индекс элемента или -(точка вставки) - 1 Список не отсортирован (или отсортирован другим компаратором) — результат не определён
reverse(list) Переворачивает порядок элементов Пустой список или из одного элемента — ничего не меняется
shuffle(list), shuffle(list, rnd) Случайно переставляет элементы С new Random(seed) порядок одинаков при каждом запуске
swap(list, i, j) Меняет местами два элемента Индекс вне [0, size()) — IndexOutOfBoundsException
fill(list, obj) Заменяет все элементы одним значением Размер не меняет: пустой список останется пустым
nCopies(n, obj) Неизменяемый список из n одинаковых элементов n < 0 — IllegalArgumentException; add() — UnsupportedOperationException
max(coll), min(coll), версии с cmp Наибольший / наименьший элемент Пустая коллекция — NoSuchElementException
copy(dest, src) Копирует элементы src поверх начала dest dest.size() < src.size() — IndexOutOfBoundsException: Source does not fit in dest
rotate(list, distance) Циклически сдвигает элементы на distance позиций Отрицательное значение сдвигает влево; distance берётся по модулю размера
frequency(coll, obj) Считает, сколько раз элемент встречается в коллекции Сравнение через equals(); obj = null допустим
disjoint(c1, c2) true, если у коллекций нет общих элементов Две пустые коллекции — true
addAll(coll, e1, e2, ...) Добавляет перечисленные элементы, возвращает true, если коллекция изменилась Список из Arrays.asList() — UnsupportedOperationException
reverseOrder(), reverseOrder(cmp) Компаратор, обратный естественному порядку или переданному компаратору Эквивалент Comparator.reverseOrder()
emptyList(), singletonList(obj) Неизменяемый пустой список / список из одного элемента add() — UnsupportedOperationException
unmodifiableList(list) Представление только для чтения Изменения исходного списка видны через обёртку
synchronizedList(list) Обёртка с синхронизацией каждого метода Перебор нужно вручную заключать в synchronized (list) { ... }
checkedList(list, type) Проверяет тип каждого добавляемого элемента Неверный тип — ClassCastException в момент вставки

Почти у каждого метода есть аналоги для других типов коллекций: emptySet(), emptyMap(), unmodifiableSet(), synchronizedMap(), checkedMap() и так далее. Примеры ниже используют Arrays.asList() — это короткий способ получить список фиксированного размера, который поддерживает set(), а значит, и все алгоритмы, переставляющие элементы.

Какие списки можно передавать в Collections

sort(), reverse(), shuffle(), swap(), fill() и rotate() меняют список через set(), поэтому работают с ArrayList, LinkedList и Arrays.asList(). Списки из List.of(), nCopies() и unmodifiableList() на любую перестановку отвечают UnsupportedOperationException — сначала сделайте копию: new ArrayList<>(List.of(...)). Почему Arrays.asList() связан с исходным массивом, показано в уроке Backed Collections.

Метод Collections.sort()

Метод Collections.sort(list) сортирует список на месте в естественном порядке (элементы должны реализовывать Comparable), а Collections.sort(list, comparator) — в порядке, заданном компаратором. Сортировка стабильна: равные элементы сохраняют исходный взаимный порядок. Для сортировки по убыванию передают 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("красный", "синий", "зеленый");
        System.out.println("Перед сортировкой: " + list);
        Collections.sort(list);
        System.out.println("После сортировки: " + list);
        Collections.sort(list, Collections.reverseOrder());
        System.out.println("После обратной сортировки: " + list);
    }
}

Вывод:

Перед сортировкой: [красный, синий, зеленый]
После сортировки: [зеленый, красный, синий]
После обратной сортировки: [синий, красный, зеленый]

Строки сравниваются по кодам символов UTF-16, а не по словарю, — поэтому заглавные буквы и буква «ё» могут оказаться не там, где ожидаешь (разбор — в уроке Comparable и Comparator). С Java 8 Collections.sort(list) просто вызывает list.sort(null); чем отличаются эти записи, объяснено в уроке List и ArrayList. Как собрать компаратор из нескольких полей, показано в разделе Comparator.comparing, thenComparing, reversed.

Метод Collections.binarySearch()

Метод Collections.binarySearch(list, key) ищет элемент в отсортированном списке бинарным поиском и возвращает его индекс. Если элемента нет, результат отрицательный: -(точка вставки) - 1, где точка вставки — позиция, на которой элемент стоял бы в отсортированном списке. Для списков с произвольным доступом (ArrayList) поиск занимает 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("красный", "синий", "зеленый");
        Collections.sort(list);

        System.out.println(list);
        System.out.println(Collections.binarySearch(list, "красный"));
        System.out.println(Collections.binarySearch(list, "черный"));
    }
}

Вывод:

[зеленый, красный, синий]
1
-4

«черный» встал бы после «синий», в позицию 3, поэтому метод вернул -(3) - 1 = -4. Из отрицательного результата точку вставки получают так: -result - 1.

Что выведет этот код, если забыть отсортировать список?

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("красный", "синий", "зеленый");
        System.out.println(list.indexOf("зеленый"));
        System.out.println(Collections.binarySearch(list, "зеленый"));
    }
}

Вывод:

2
-1

Бинарный поиск сравнил «зеленый» со средним элементом «синий», решил, что искать нужно левее, потом сравнил с «красный» и снова ушёл влево — до элемента в позиции 2 он так и не дошёл. Java не проверяет, отсортирован ли список, поэтому ошибка проявляется только неверным результатом. То же правило действует для компаратора: если список отсортирован через Collections.reverseOrder(), искать нужно с тем же компаратором — Collections.binarySearch(list, key, Collections.reverseOrder()).

Методы reverse(), shuffle() и swap()

Метод Collections.reverse(list) переворачивает порядок элементов списка, Collections.shuffle(list) переставляет их в случайном порядке, а Collections.swap(list, i, j) меняет местами два элемента с индексами i и j. Все три метода изменяют сам список и ничего не возвращают.

import java.util.Arrays;
import java.util.Collections;
import java.util.List;

public class ReverseShuffleExample {
    public static void main(String[] args) {
        List<String> list = Arrays.asList("красный", "синий", "зеленый", "черный");
        System.out.println("Исходный список: " + list);
        Collections.reverse(list);
        System.out.println("После reverse(): " + list);
        Collections.swap(list, 0, 3);
        System.out.println("После swap(0, 3): " + list);
        Collections.shuffle(list);
        System.out.println("После shuffle(): " + list);
    }
}

Вывод (последняя строка при каждом запуске своя):

Исходный список: [красный, синий, зеленый, черный]
После reverse(): [черный, зеленый, синий, красный]
После swap(0, 3): [красный, зеленый, синий, черный]
После shuffle(): [синий, черный, красный, зеленый]

По умолчанию shuffle() берёт внутренний генератор случайных чисел. Если нужно, чтобы перемешивание повторялось от запуска к запуску (тесты, воспроизведение бага, одинаковая колода в игре), передайте свой генератор с фиксированным начальным значением: Collections.shuffle(list, new Random(42)) — для одного и того же исходного списка порядок всегда будет одинаковым. Вызов Collections.swap(list, 0, 4) на списке из четырёх элементов бросит IndexOutOfBoundsException.

Методы fill() и nCopies()

Метод Collections.fill(list, obj) заменяет каждый элемент списка значением obj, но не добавляет новых элементов. Чтобы создать список из n одинаковых значений, используют Collections.nCopies(n, obj): он возвращает неизменяемый список, который обычно сразу копируют в ArrayList.

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("красный", "синий", "зеленый");
        Collections.fill(colors, "черный");
        System.out.println(colors);

        List<String> empty = new ArrayList<>();
        Collections.fill(empty, "черный");
        System.out.println(empty);

        List<String> filled = new ArrayList<>(Collections.nCopies(3, "черный"));
        filled.add("белый");
        System.out.println(filled);
    }
}

Вывод:

[черный, черный, черный]
[]
[черный, черный, черный, белый]

Пустой список после fill() остался пустым: заполнять нечего. Список из nCopies() без копирования не изменить — add() бросит UnsupportedOperationException, а nCopies(-1, "x") — IllegalArgumentException. Все n позиций ссылаются на один и тот же объект, поэтому для изменяемых объектов (например, nCopies(3, new StringBuilder())) изменение одного элемента видно во всех.

Методы Collections.max() и min()

Методы Collections.max(collection) и Collections.min(collection) возвращают наибольший и наименьший элемент любой коллекции в естественном порядке, а перегрузки с компаратором — по заданному правилу. Коллекция перебирается целиком за O(n), сортировать её заранее не нужно. Для пустой коллекции оба метода бросают 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("кот", "собака", "ёж");
        System.out.println(Collections.max(words, Comparator.comparingInt(String::length)));
        System.out.println(Collections.max(words));
    }
}

Вывод:

9
2
собака
ёж

Последняя строка удивляет: «ёж» оказался «больше» «собаки», потому что код буквы «ё» (U+0451) больше кода «с» (U+0441). Вызов Collections.max(new ArrayList<Integer>()) завершится исключением java.util.NoSuchElementException — если коллекция может быть пустой, проверьте isEmpty() заранее или используйте stream().max(cmp), который возвращает Optional.

Метод Collections.copy()

Метод Collections.copy(dest, src) записывает элементы списка src поверх первых позиций списка dest; остальные элементы dest не меняются. Размер dest должен быть не меньше размера src — иначе метод бросает IndexOutOfBoundsException. Ёмкость ArrayList здесь не считается, важен только size().

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);
    }
}

Вывод:

[1, 2, 3, 7]
Exception in thread "main" java.lang.IndexOutOfBoundsException: Source does not fit in dest

Список new ArrayList<>(10) только выглядит вместительным: его size() равен 0. Если нужна независимая копия списка, а не перезапись существующего, пишут new ArrayList<>(src) или неизменяемый List.copyOf(src) (Java 10).

Метод Collections.rotate()

Метод Collections.rotate(list, distance) циклически сдвигает элементы списка: элемент с индексом i переходит на позицию (i + distance) % size. Положительное distance сдвигает элементы вправо, отрицательное — влево, а элементы, вышедшие за конец списка, появляются в начале.

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);
    }
}

Вывод:

[a, b, c, d, e]
[d, e, a, b, c]
[e, a, b, c, d]
[a, c, d, b, e]

Сдвиг больше размера списка не ошибка: rotate(list, 7) для пяти элементов даёт тот же результат, что rotate(list, 2). Последний пример — приём из документации JDK: rotate() для подсписка переносит элемент "b" с позиции 1 на позицию 3, не трогая остальную часть списка.

Методы frequency() и disjoint()

Метод Collections.frequency(collection, obj) возвращает, сколько раз элемент встречается в коллекции, сравнивая элементы через equals(). Метод Collections.disjoint(c1, c2) возвращает true, если у двух коллекций нет ни одного общего элемента. Оба метода работают с любой Collection, не только со списками.

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("красный", "синий", "красный", null);
        System.out.println(Collections.frequency(colors, "красный"));
        System.out.println(Collections.frequency(colors, "Красный"));
        System.out.println(Collections.frequency(colors, null));

        List<String> warm = List.of("красный", "оранжевый");
        List<String> cold = List.of("синий", "голубой");
        System.out.println(Collections.disjoint(colors, warm));
        System.out.println(Collections.disjoint(warm, cold));
    }
}

Вывод:

2
0
1
false
true

«Красный» с заглавной буквы не найден: equals() учитывает регистр. Для собственных классов frequency() считает правильно, только если в них переопределён метод equals(). Метод проходит коллекцию целиком, поэтому подсчёт частоты каждого элемента в цикле занимает O(n²) — для больших данных удобнее HashMap со счётчиками.

Создание списков: addAll(), emptyList(), singletonList()

Метод Collections.addAll(collection, e1, e2, ...) добавляет перечисленные элементы в существующую коллекцию и возвращает true, если она изменилась. Методы emptyList() и singletonList(obj) возвращают неизменяемые списки из нуля и одного элемента — их используют как возвращаемое значение, чтобы не создавать лишний ArrayList.

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");
    }
}

Вывод:

true
[a, b, c]
[] [a]
Exception in thread "main" java.lang.UnsupportedOperationException

Для Set метод addAll() вернёт false, если все элементы там уже были. Начиная с Java 9 вместо emptyList() и singletonList() часто пишут List.of() и List.of(obj); разница в том, что singletonList(null) допустим, а List.of(null) бросает NullPointerException.

Обёртки checkedList(), unmodifiableList(), synchronizedList()

Обёртки класса Collections возвращают новое представление того же списка с дополнительным поведением: checkedList() проверяет тип каждого добавляемого элемента, unmodifiableList() запрещает изменения, synchronizedList() синхронизирует методы. Данные не копируются — обёртка работает с исходным списком, поэтому изменения, сделанные в обход неё, видны через обёртку.

Проверяемая обёртка (checkedList(), checkedCollection(), checkedMap()) нужна там, где generics не защищают: код с raw-типами или старая библиотека может положить в List<String> число, и ошибка проявится далеко от места вставки. Обёртка бросает исключение сразу:

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);      // попадёт в список без проверки
        System.out.println(names);

        addRaw(checked, 20);    // ClassCastException
    }

    // имитирует старый код без generics
    @SuppressWarnings({"rawtypes", "unchecked"})
    static void addRaw(List list, Object value) {
        list.add(value);
    }
}

Вывод:

[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

Чем unmodifiableList() отличается от List.of() и List.copyOf(), сравнивается в таблице раздела Неизменяемые коллекции, а когда вместо synchronizedList() и synchronizedMap() лучше взять CopyOnWriteArrayList или ConcurrentHashMap — в разделе Потокобезопасные коллекции Java.

Новое в Java 21

Вместе с интерфейсами SequencedCollection, SequencedSet и SequencedMap (JEP 431) в класс Collections добавлены обёртки unmodifiableSequencedCollection(), unmodifiableSequencedSet() и unmodifiableSequencedMap(). Они сохраняют методы getFirst(), getLast() и reversed(), которых нет у обычной unmodifiableCollection(). На Java 17 этих методов нет.

Часто задаваемые вопросы

Как найти самый частый элемент списка с помощью Collections?

Комбинацией max() и frequency(): Collections.max(list, Comparator.comparingInt(x -> Collections.frequency(list, x))). Для [a, b, a, c, b, a] результат — a; при равной частоте вернётся элемент, встретившийся первым. Это O(n²), поэтому для больших списков частоты считают за один проход в HashMap. Как работает frequency() — в разделе Методы frequency() и disjoint().

Как перевернуть список, не изменяя исходный?

Collections.reverse() меняет сам список, поэтому сначала делают копию: List<String> copy = new ArrayList<>(list); Collections.reverse(copy);. В Java 21 есть list.reversed() — он возвращает представление в обратном порядке без копирования, но изменения через него попадают в исходный список.

Зачем нужен Collections.emptyList(), если можно вернуть new ArrayList<>()?

emptyList() возвращает один общий неизменяемый объект, поэтому не тратит память при каждом вызове и защищает от случайного изменения: вызывающий код получит UnsupportedOperationException при add(). Если вызывающему коду нужно наполнять результат, возвращайте new ArrayList<>().

Работают ли методы Collections с массивами и Map?

С массивами — нет: для них есть аналогичный класс Arrays (Arrays.sort(), Arrays.binarySearch(), Arrays.fill()), а массив объектов можно обернуть в список через Arrays.asList(). Алгоритмы вроде sort() и shuffle() к Map не применяются, но для отображений есть обёртки: unmodifiableMap(), synchronizedMap(), checkedMap(), emptyMap(), singletonMap().

Комментарии

Зарегистрируйтесь или войдите, чтобы иметь возможность оставить комментарий.