Нужно удалить из массива все чётные элементы. Какой вызов изменит исходный массив, а какой создаст отдельны...

Нужно удалить из массива все чётные элементы. Какой вызов изменит исходный массив, а какой создаст отдельный результат?

var numbers = [1, 2, 3, 4, 5]
let remaining = numbers.filter { $0 % 2 != 0 }
print(numbers)

numbers.removeAll { $0 % 2 == 0 }
print(numbers)
print(remaining)
Проходите собеседования с ИИ помощником Hintsage

Краткий ответ

filter не изменяет исходный массив: он создаёт и возвращает новый массив с подходящими элементами. removeAll(where:) изменяет массив на месте, удаляя элементы, для которых предикат возвращает true.

До вызова removeAll код напечатает [1, 2, 3, 4, 5]. После него numbers станет [1, 3, 5], а remaining останется независимым результатом [1, 3, 5].

Исторический контекст

В коллекциях нужны оба стиля работы. Функциональные операции вроде filter позволяют строить новый результат без изменения исходного значения, а мути­рующие операции вроде removeAll(where:) полезны, когда существующую коллекцию нужно обновить на месте.

Такое разделение согласуется с семантикой значений Swift: выражение преобразования обычно создаёт новое значение, тогда как метод с мутацией явно отражает изменение переменной.

Постановка проблемы

Если использовать filter для удаления элементов из большого массива, появится новый массив, а исходный останется в памяти до тех пор, пока на него существуют ссылки или переменные. Это может быть лишней затратой памяти и копированием данных.

Если применить removeAll(where:), исходная переменная должна быть объявлена через var. Нельзя вызвать мути­рующий метод у let, а при наличии разделяемого буфера Swift может выполнить копирование из-за copy-on-write.

Подробное решение

filter перебирает исходный массив, передаёт каждый элемент предикату и добавляет в новый результат только элементы, для которых предикат вернул true. Поэтому numbers после первой операции не меняется, а remaining получает отдельное значение массива.

removeAll(where:) перебирает элементы и удаляет те, для которых предикат истинен. Оставшиеся элементы сохраняют относительный порядок. Для Array операция обычно выполняется в существующем буфере, но нельзя рассматривать это как безусловную гарантию отсутствия выделений: при разделяемом хранилище семантика значений может потребовать копирования.

Обе операции имеют линейную асимптотику O(n), поскольку в общем случае необходимо проверить каждый элемент. Главное различие не в сложности, а в направлении преобразования: filter создаёт результат, removeAll(where:) меняет коллекцию.

var numbers = [1, 2, 3, 4, 5] let remaining = numbers.filter { $0 % 2 != 0 } print(numbers) // [1, 2, 3, 4, 5] numbers.removeAll { $0 % 2 == 0 } print(numbers) // [1, 3, 5] print(remaining) // [1, 3, 5]

После filter две переменные содержат одинаковые элементы, но изменение numbers не изменяет remaining. Это обеспечивается семантикой значений и механизмом copy-on-write.

Ситуация из практики

В обработчике данных нужно удалить устаревшие записи из большого рабочего массива и продолжить использовать ту же переменную. Вариант с filter проще читать, но создаёт новый массив и временно сохраняет старый и новый наборы данных.

Вариант с removeAll(where:) лучше выражает намерение изменить рабочую коллекцию и обычно снижает лишние расходы. Однако если исходный массив разделяет хранилище с другой переменной, Swift сохранит корректную семантику значений, скопировав данные при необходимости.

Выбор removeAll(where:) обоснован, когда исходный массив больше не нужен в прежнем виде. Если же исходные данные должны сохраниться для другого вычисления, следует выбрать filter, потому что немутирующее преобразование делает эту зависимость явной.

Что кандидаты часто упускают

  1. Сохраняет ли removeAll(where:) порядок оставшихся элементов?

Да. Оставшиеся элементы сохраняют относительный порядок. Если порядок не важен и требуется оптимизировать удаление одного элемента, иногда применяют другие операции, например замену удаляемого элемента последним с последующим удалением, но это уже другая семантика и не эквивалентно removeAll(where:).

  1. Всегда ли filter физически копирует элементы исходного массива?

filter возвращает новый массив с выбранными элементами, поэтому результат логически независим от исходного массива. Swift может оптимизировать внутренние операции, но полагаться на отсутствие копирования нельзя: публичный контракт гарантирует результат и семантику, а не конкретную стратегию управления памятью.

  1. Что произойдёт, если две переменные разделяют один массив?

До мутации Swift может использовать общий буфер благодаря оптимизации copy-on-write. При вызове removeAll(where:) через одну переменную, если буфер разделяется, Swift создаст копию перед изменением; другая переменная продолжит видеть прежнее содержимое. Если буфер уникален, изменение может выполняться непосредственно в нём.