Какой компромисс определяет выбор между sorted(by:) и sort(by:) для массива?
sorted(by:) сохраняет исходный массив и возвращает отсортированный результат, поэтому подходит для немутирующего конвейера обработки. sort(by:) изменяет массив на месте, что обычно уменьшает количество создаваемых промежуточных значений, но требует изменяемого массива и уничтожает его исходный порядок.
В стандартной библиотеке Swift отдельно представлены операции, сохраняющие исходную коллекцию, и операции, изменяющие её. Такое разделение поддерживает одновременно функциональный стиль обработки данных и эффективные алгоритмы на месте.
Для массивов это особенно важно: иногда порядок исходных данных является частью состояния приложения, а иногда массив служит временным буфером и может быть безопасно изменён.
Выбор неподходящей операции может привести к потере исходного порядка элементов, неожиданной мутации общего состояния или дополнительному выделению памяти. При этом название sort не означает гарантированное отсутствие аллокаций: из-за семантики значений и механизма copy-on-write операция может создать отдельное хранилище.
Нужно также учитывать тип ссылки на массив: let нельзя передать как изменяемый массив для sort(by:), тогда как результат sorted(by:) можно получить из неизменяемого значения.
sorted(by:) не меняет исходную коллекцию. Для массива он возвращает новый отсортированный массив, поэтому исходное значение можно использовать повторно в первоначальном порядке. Это удобнее в цепочках преобразований, при подготовке представления данных и при работе с состоянием, которым нельзя управлять через побочные эффекты.
sort(by:) является мутирующей операцией. Она переставляет элементы самого массива, требует переменную коллекцию и обычно выбирается для временных данных, когда прежний порядок больше не нужен. Главный выигрыш — отсутствие необходимости хранить отдельный логический результат сортировки, хотя фактические затраты памяти зависят от совместного использования хранилища и реализации.
Обе операции используют переданное замыкание для сравнения элементов. Замыкание должно задавать строгий слабый порядок: сравнение не должно противоречить самому себе, а отношения между элементами должны быть согласованными. Нарушение этого требования делает результат сортировки ненадёжным.
Минимальный пример:
В первом случае создаётся отсортированный результат, а source не меняется. Во втором случае изменяется buffer; присваивание let сделало бы такой вызов невозможным.
Исходный массив товаров хранится в состоянии экрана в порядке, заданном сервером, а пользователю нужно показать его по цене. Вариант с sort(by:) прост, но меняет состояние и может нарушить логику повторного использования исходного порядка; копирование вручную перед сортировкой добавляет лишний шаг и усложняет намерение.
Вариант с sorted(by:) явно создаёт представление для отображения, сохраняя источник. Его минус — необходимость результата с отдельным набором элементов и потенциальные расходы на память и сравнения. Для временного большого буфера, который после сортировки больше не нужен в исходном виде, разумнее выбрать sort(by:); для данных приложения был бы выбран sorted(by:), поскольку сохранение исходного значения важнее потенциальной экономии.
Что произойдёт при сортировке массива, которым разделяют несколько переменных?
Массивы имеют семантику значений и используют copy-on-write. Если два значения разделяют одно внутреннее хранилище, мутация через sort(by:) должна отделить хранилище, чтобы второе значение не изменилось. Поэтому «сортировка на месте» означает мутацию логического значения, но не гарантирует отсутствие физического копирования.
Можно ли использовать оператор сравнения <= вместо <?
Обычно нет: при равных элементах a <= b и b <= a одновременно дают true, что нарушает требование строгого слабого порядка для компаратора. Следует использовать строгое сравнение либо компаратор, корректно обрабатывающий равенство; иначе порядок и результат могут быть непредсказуемыми.
Гарантирует ли сортировка сохранение порядка равных элементов?
Нет, полагаться на стабильность сортировки нельзя, если это специально не обеспечено отдельным алгоритмом или дополнительным ключом. Если порядок равных элементов важен, его нужно выразить явно, например включить исходную позицию в ключ сравнения или выбрать алгоритм с гарантией стабильности.