Сортировка массива — это перестановка его элементов в порядке возрастания или убывания по выбранному признаку: числу, строке, дате или полю объекта. На практике массив сортируют не самописным алгоритмом, а встроенной функцией языка: sorted() и list.sort() в Python, arr.sort() и arr.toSorted() в JavaScript, Arrays.sort() и List.sort() в Java. Они работают за O(n log n) в худшем случае и уже отлажены, поэтому писать свою сортировку в рабочем коде обычно незачем.
Содержание
- Четыре понятия, без которых сортировку не настроить
- Шпаргалка по трем языкам
- Python: sorted, list.sort и параметр key
- JavaScript: sort, toSorted и ловушка «сортировки как строк»
- Java: Arrays.sort и Comparator
- Сложность и память: что обещают встроенные сортировки
- Когда встроенной сортировки мало
- Если не получилось: симптом -> причина -> что делать
- Выводы
- Где применяется / связь с практикой
- FAQ
Ниже — как вызвать сортировку в каждом из трех языков, как задать порядок ключом или компаратором, что такое стабильность и почему [10, 9, 1].sort() в JavaScript дает «неправильный» ответ. Примеры проверены 24.09.2026 на Python 3.14.7, Node.js 24.21 и OpenJDK 25.0.4.
Четыре понятия, без которых сортировку не настроить
- На месте или копия. Сортировка на месте меняет исходный массив (
list.sort(),arr.sort(),Arrays.sort()); копирующая возвращает новый, исходный не трогает (sorted(),toSorted()). - Ключ — значение, которое сортировка вычисляет из элемента и сравнивает вместо него: длину строки, поле
score, строку в нижнем регистре. - Компаратор — функция от двух элементов, которая отвечает «кто раньше»: отрицательное число — первый раньше, ноль — равны, положительное — второй раньше.
- Стабильность — элементы с равными ключами сохраняют исходный взаимный порядок. Это важно, когда сортируют в несколько проходов или по одному полю из многих.
Шпаргалка по трем языкам
| Задача | Python | JavaScript | Java |
|---|---|---|---|
| На месте | a.sort() |
a.sort(cmp) |
Arrays.sort(a), list.sort(cmp) |
| Новая копия | sorted(a) |
a.toSorted(cmp) |
a.clone(), затем Arrays.sort |
| По убыванию | reverse=True |
(x, y) => y - x |
Comparator.reverseOrder() |
| По признаку | key=... |
компаратор | Comparator.comparing(...) |
| Стабильна | да, гарантировано | да, со стандарта ES2019 | объекты — да; примитивы — не важно |
| По умолчанию | естественный порядок | как строки | естественный порядок |
Последняя строка — главный источник ошибок, к ней вернемся в разделе про JavaScript.
Python: sorted, list.sort и параметр key
Минимальный полный пример: числа, убывание и сортировка записей по двум полям.
nums = [5, 2, 9, 1, 5, 6]
print(sorted(nums)) # новый список
print(nums) # исходный не изменился
nums.sort(reverse=True) # сортировка на месте
print(nums)
students = [
{"name": "Оля", "group": 2, "score": 90},
{"name": "Иван", "group": 1, "score": 75},
{"name": "Петр", "group": 2, "score": 75},
{"name": "Анна", "group": 1, "score": 90},
]
# по баллам по убыванию, при равенстве - по имени по возрастанию
res = sorted(students, key=lambda s: (-s["score"], s["name"]))
print([s["name"] for s in res])
[1, 2, 5, 5, 6, 9]
[5, 2, 9, 1, 5, 6]
[9, 6, 5, 5, 2, 1]
['Анна', 'Оля', 'Иван', 'Петр']
Разбор по строкам. sorted() принимает любой итерируемый объект и возвращает новый список. list.sort() меняет список и возвращает None, поэтому nums = nums.sort() — частая ошибка: в переменной окажется None.
Ключ-кортеж (-score, name) сравнивается поэлементно: сначала баллы (минус превращает возрастание в убывание), при равенстве — имя. Минус работает только для чисел. Для строк по убыванию используют два стабильных прохода: сначала по второстепенному полю, потом по главному с reverse=True.
Функция key вызывается один раз на элемент, а не на каждое сравнение, поэтому дорогое вычисление в ключе обходится дешевле, чем в компараторе. Компаратор в стиле «сравни два» в Python 3 тоже доступен — через functools.cmp_to_key, когда правило не выражается ключом.
Стабильность на примере
rows = [("Иван", 1), ("Анна", 2), ("Петр", 1), ("Оля", 2)]
by_group = sorted(rows, key=lambda r: r[1])
print(by_group)
[('Иван', 1), ('Петр', 1), ('Анна', 2), ('Оля', 2)]
Внутри группы 1 Иван остался перед Петром, как во входе. Это гарантия языка, а не совпадение: на нее можно опираться.
Строки: регистр и буква «е» с точками
По умолчанию строки сравниваются по кодовым точкам Unicode. Заглавные кириллические буквы идут раньше строчных, а буква U+0451 («е» с двумя точками) стоит после всего алфавита «а-я»: слово «еж», написанное через нее, уходит в конец списка.
words = ["яблоко", "Банан", "ежевика", "ёж", "арбуз"]
print(sorted(words))
print(sorted(words, key=str.casefold))
print(sorted(words, key=lambda w: w.casefold().replace("ё", "е")))
['Банан', 'арбуз', 'ежевика', 'яблоко', 'ёж']
['арбуз', 'Банан', 'ежевика', 'яблоко', 'ёж']
['арбуз', 'Банан', 'ёж', 'ежевика', 'яблоко']
Замена U+0451 на обычную «е» в ключе — упрощение для русского текста. Полноценный порядок по правилам языка дает библиотека сопоставления с учетом локали (например, ICU); стандартный locale.strxfrm зависит от настроек ОС.
JavaScript: sort, toSorted и ловушка «сортировки как строк»
Без компаратора Array.prototype.sort приводит элементы к строкам и сравнивает их посимвольно. Для чисел это почти всегда не то, что нужно.
const nums = [10, 9, 1, 100, 25];
console.log(nums.sort()); // ловушка
console.log(nums.sort((a, b) => a - b)); // числа по возрастанию
const src = [3, 1, 2];
const copy = src.toSorted((a, b) => b - a);
console.log(copy, src);
const users = [
{ name: "Оля", age: 30 },
{ name: "Иван", age: 25 },
{ name: "Петр", age: 30 },
];
users.sort((a, b) => b.age - a.age || a.name.localeCompare(b.name, "ru"));
console.log(users.map(u => u.name).join(", "));
[ 1, 10, 100, 25, 9 ]
[ 1, 9, 10, 25, 100 ]
[ 3, 2, 1 ] [ 3, 1, 2 ]
Оля, Петр, Иван
Первая строка вывода: «100» раньше «25», потому что символ «1» меньше «2». Исправление — компаратор (a, b) => a - b. Он корректен для обычных чисел, но не для NaN: с ним результат сравнения не определен, такие значения лучше отфильтровать заранее.
sort() меняет массив на месте и возвращает ссылку на него же. toSorted() (ES2023, есть в Node.js 20+ и актуальных браузерах) возвращает копию. В старых средах аналог — [...src].sort(cmp).
Сортировка по нескольким полям в JS строится через ||: если первая разница равна 0, берется вторая. Для строк — localeCompare с локалью, а не <: он учитывает регистр и алфавит языка.
const words = ["яблоко", "Банан", "ёж", "ежевика", "арбуз"];
console.log(words.toSorted());
console.log(words.toSorted((a, b) => a.localeCompare(b, "ru")));
[ 'Банан', 'арбуз', 'ежевика', 'яблоко', 'ёж' ]
[ 'арбуз', 'Банан', 'ёж', 'ежевика', 'яблоко' ]
Частая ошибка — компаратор, возвращающий true/false вместо числа: [3, 1, 2].sort((a, b) => a > b) в Node.js 24 вернул [ 3, 1, 2 ] без изменений. false превращается в 0 («равны»), и движок не получает сигнала «первый раньше». Результат такой ошибки зависит от движка и входа, поэтому компаратор всегда должен возвращать число.
Java: Arrays.sort и Comparator
import java.util.*;
public class SortDemo {
record Student(String name, int group, int score) {}
public static void main(String[] args) {
int[] nums = {5, 2, 9, 1, 5, 6};
Arrays.sort(nums);
System.out.println(Arrays.toString(nums));
Integer[] boxed = {5, 2, 9, 1, 5, 6};
Arrays.sort(boxed, Comparator.reverseOrder());
System.out.println(Arrays.toString(boxed));
List<Student> list = new ArrayList<>(List.of(
new Student("Оля", 2, 90), new Student("Иван", 1, 75),
new Student("Петр", 2, 75), new Student("Анна", 1, 90)));
list.sort(Comparator.comparingInt(Student::score).reversed()
.thenComparing(Student::name));
list.forEach(s -> System.out.print(s.name() + " "));
System.out.println();
Integer[] big = {-2_000_000_000, 2_000_000_000, 0};
Arrays.sort(big, (a, b) -> a - b);
System.out.println("a - b: " + Arrays.toString(big));
Arrays.sort(big, Integer::compare);
System.out.println("Integer.compare: " + Arrays.toString(big));
}
}
[1, 2, 5, 5, 6, 9]
[9, 6, 5, 5, 2, 1]
Анна Оля Иван Петр
a - b: [0, 2000000000, -2000000000]
Integer.compare: [-2000000000, 0, 2000000000]
Файл запускается одной командой java SortDemo.java (Java 11+; record — с Java 16). Что важно:
Arrays.sort(int[])сортирует только по возрастанию: компаратор к массиву примитивов не передать. Для убывания берутInteger[]сComparator.reverseOrder()или сортируют и разворачивают массив.- Для примитивов используется Dual-Pivot Quicksort, он нестабилен, но у равных чисел нечего различать. Для объектов (
Arrays.sort(Object[]),List.sort) используется стабильная сортировка на основе TimSort, это записано в документации. - Компаратор
(a, b) -> a - bломается на больших числах: разность2 000 000 000 - (-2 000 000 000)переполняетint, меняет знак, и массив получается неотсортированным. Правильно —Integer.compareилиComparator.comparingInt.
Сложность и память: что обещают встроенные сортировки
| Реализация | Время, худший случай | Уже отсортированный вход | Доп. память | Стабильна |
|---|---|---|---|---|
Python sort/sorted |
O(n log n) | O(n) | до ~n/2 ссылок | да |
JS sort в V8 (TimSort) |
O(n log n) | O(n) | до ~n/2 | да |
Java Arrays.sort(int[]) |
O(n log n) | зависит от входа | O(log n) и выше | не важно |
Java объекты, List.sort |
O(n log n) | O(n) | до ~n/2 | да |
O(n log n) — нижняя граница для любой сортировки, которая только сравнивает элементы: быстрее в худшем случае такие алгоритмы не бывают. Реальное время еще зависит от стоимости сравнения: компаратор с localeCompare или ключ с тяжелым вычислением замедляет сортировку сильнее, чем сам алгоритм.
Как устроены пузырек, вставки, быстрая сортировка и слияние изнутри, с прогоном по шагам — в статье Сортировка: основные принципы и сравнение алгоритмов.
Когда встроенной сортировки мало
- Нужны только k лучших из большого массива — не сортируйте все:
heapq.nlargest(3, prices)в Python работает за O(n log k). - Целые числа в малом диапазоне (оценки 1-5, возраст 0-120) — сортировка подсчетом дает O(n + m) без сравнений, где m — число возможных значений.
- Данные не помещаются в память — внешняя сортировка слиянием по частям или
ORDER BYв базе данных, где есть индекс. - Учебная задача или собеседование — просят реализовать алгоритм руками; в рабочем коде это исключение.
import heapq
prices = [420, 15, 999, 73, 250, 610, 8]
print(heapq.nlargest(3, prices))
[999, 610, 420]
Если не получилось: симптом -> причина -> что делать
| Симптом | Причина | Исправление |
|---|---|---|
JS: [1, 10, 100, 25, 9] |
нет компаратора, сравнение как строк | (a, b) => a - b |
Python: в переменной None |
присвоили результат list.sort() |
sorted() или вызов без присваивания |
Python: TypeError: '<' not supported between instances of 'str' and 'int' |
в списке смешаны типы | привести к одному типу или задать key |
| Java: порядок «прыгает» на больших числах | переполнение в a - b |
Integer.compare |
| Заглавные раньше строчных, «е» с точками в конце | сравнение по кодовым точкам | casefold плюс замена «е» с точками в ключе (одного casefold мало) или localeCompare(..., "ru") |
| JS: массив не изменился | компаратор вернул true/false |
возвращать число |
Выводы
- Для сортировки массива в рабочем коде берите встроенную функцию: она работает за O(n log n), а в Python, JavaScript и для объектов в Java еще и стабильна.
- Различайте сортировку на месте (
sort) и копию (sorted,toSorted);list.sort()возвращаетNone. - Порядок задают ключом (Python,
Comparator.comparingв Java) или компаратором, который возвращает число, а не булево значение. - В JavaScript без компаратора числа сортируются как строки; в Java
a - bв компараторе переполняется — используйтеInteger.compare. - Строки на русском сортируйте без учета регистра (ключ
casefold) и с правильным местом «е» с точками (замена в ключе илиlocaleCompareс локальюru).
Где применяется / связь с практикой
Освойте тему на практике
Сортировка — часть почти любой задачи с данными: ранжирование выдачи, топ-N товаров, группировка записей перед слиянием, подготовка к бинарному поиску. На собеседованиях ее проверяют с двух сторон: умеет ли кандидат правильно вызвать встроенную функцию с ключом и почему она работает за O(n log n). Разобрать сортировки, кучи и оценку сложности на задачах можно на курсе «Алгоритмы и структуры данных», а посмотреть формат занятий — на открытых уроках Otus.
FAQ
Можно ли отсортировать словарь в Python?
Сам словарь хранит порядок вставки, но метода sort у него нет. Берут sorted(d.items(), key=lambda kv: kv[1]) и при необходимости собирают новый словарь через dict(...).
Как перемешать массив, а не отсортировать?
Сортировка со случайным компаратором дает неравномерное перемешивание. Правильно — random.shuffle в Python, Collections.shuffle в Java или алгоритм Фишера-Йетса в JavaScript.
Нужно ли сортировать массив перед поиском элемента?
Только если поисков будет много: сортировка стоит O(n log n), зато каждый бинарный поиск потом — O(log n). Для одного поиска достаточно линейного прохода за O(n).



