В Какао, если я хочу перебрать NSMutableArray и удалить несколько объектов, которые соответствуют определенным критериям, каков наилучший способ сделать это без перезапуска цикла каждый раз, когда я удаляю объект?
Спасибо,
Изменить: Просто чтобы уточнить - я искал лучший способ, например, что-то более элегантное, чем обновление индекса вручную, в котором я нахожусь. Например, в C ++ я могу сделать;
iterator it = someList.begin();
while (it != someList.end())
{
if (shouldRemove(it))
it = someList.erase(it);
}
objective-c
cocoa
Эндрю Грант
источник
источник
Ответы:
Для ясности я хотел бы сделать начальный цикл, где я собираю элементы для удаления. Затем я удаляю их. Вот пример с использованием синтаксиса Objective-C 2.0:
Тогда не возникает вопроса о том, правильно ли обновляются индексы, или о других небольших бухгалтерских деталях.
Отредактировано, чтобы добавить:
В других ответах было отмечено, что обратная формулировка должна быть быстрее. т.е. если вы перебираете массив и создаете новый массив объектов для хранения, а не объектов для отбрасывания. Это может быть правдой (хотя как насчет затрат на память и обработку при выделении нового массива и отбрасывании старого?), Но даже если он быстрее, он может оказаться не таким уж большим, как для наивной реализации, потому что NSArrays не ведите себя как "нормальные" массивы. Они говорят, говорят, но ходят по-другому. Смотрите хороший анализ здесь:
Обратная формулировка может быть быстрее, но мне никогда не нужно было заботиться о том, так ли это, потому что приведенная выше формулировка всегда была достаточно быстрой для моих нужд.
Для меня главная идея - использовать любую формулировку, которая вам наиболее понятна. Оптимизируйте только при необходимости. Я лично считаю вышеизложенную формулировку более ясной, поэтому я использую ее. Но если обратная формулировка вам понятнее, сделайте это.
источник
Еще один вариант. Таким образом, вы получите удобочитаемость и хорошую производительность:
источник
removeObjectsAtIndexes
это худший метод удаления объектов, вы согласны с этим? Я спрашиваю об этом, потому что ваш ответ сейчас слишком стар. Все-таки хорошо бы выбрать лучшее?enumerateObjectsUsingBlock:
получит вам прирост индекса бесплатно.Это очень простая проблема. Вы просто повторяете в обратном направлении:
Это очень распространенная модель.
источник
Некоторые из других ответов будут иметь плохую производительность на очень больших массивах, потому что такие методы, как
removeObject:
иremoveObjectsInArray:
предполагают, выполняют линейный поиск приемника, что является пустой тратой, потому что вы уже знаете, где находится объект. Кроме того, любой вызов долженremoveObjectAtIndex:
будет копировать значения из индекса в конец массива на один слот за раз.Более эффективным будет следующее:
Поскольку мы устанавливаем емкость
itemsToKeep
, мы не тратим время на копирование значений во время изменения размера. Мы не модифицируем массив на месте, поэтому мы можем использовать быстрое перечисление. ИспользованиеsetArray:
для замены содержимогоarray
сitemsToKeep
будет эффективным. В зависимости от вашего кода, вы можете даже заменить последнюю строку на:Так что даже копировать значения не нужно, только поменяйте местами указатель.
источник
Вы можете использовать NSpredicate для удаления элементов из вашего изменяемого массива. Это не требует петель.
Например, если у вас есть NSMutableArray из имен, вы можете создать предикат как этот:
Следующая строка оставит вас с массивом, который содержит только имена, начинающиеся с b.
Если у вас возникли проблемы с созданием необходимых предикатов, воспользуйтесь этой ссылкой для разработчиков Apple .
источник
Я сделал тест производительности, используя 4 разных метода. Каждый тест повторял все элементы в массиве из 100 000 элементов и удалял каждый 5-й элемент. Результаты не сильно изменились с / без оптимизации. Это было сделано на iPad 4:
(1)
removeObjectAtIndex:
- 271 мс(2)
removeObjectsAtIndexes:
- 1010 мс (поскольку создание набора индексов занимает ~ 700 мс; в остальном это в основном то же, что и вызов removeObjectAtIndex: для каждого элемента)(3)
removeObjects:
- 326 мс(4) создать новый массив с объектами, прошедшими тест - 17 мс
Таким образом, создание нового массива является самым быстрым. Все остальные методы сопоставимы, за исключением того, что при использовании removeObjectsAtIndexes: будет хуже с удалением большего количества элементов из-за времени, необходимого для построения набора индексов.
источник
Либо используйте цикл обратного отсчета по индексам:
или сделайте копию с объектами, которые вы хотите сохранить.
В частности, не используйте
for (id object in array)
цикл илиNSEnumerator
.источник
Для iOS 4+ или OS X 10.6+ Apple добавила
passingTest
ряд API-интерфейсовNSMutableArray
, например– indexesOfObjectsPassingTest:
. Решение с таким API будет:источник
В настоящее время вы можете использовать обратное перечисление на основе блоков. Простой пример кода:
Результат:
другой вариант с одной строкой кода:
источник
В более декларативной форме, в зависимости от критериев, соответствующих удаляемым элементам, вы можете использовать:
@ Натан должен быть очень эффективным
источник
Вот простой и чистый способ. Мне нравится дублировать мой массив прямо в вызове быстрого перечисления:
Таким образом, вы перечисляете копию удаляемого массива, в котором хранятся одинаковые объекты. NSArray содержит только объектные указатели, так что это совершенно нормально с точки зрения памяти и производительности.
источник
for (LineItem *item in self.lineItems.copy)
Добавьте объекты, которые вы хотите удалить, во второй массив и после цикла используйте -removeObjectsInArray :.
источник
это должно сделать это:
надеюсь это поможет...
источник
Почему бы вам не добавить объекты для удаления в другой NSMutableArray. Когда вы закончите итерацию, вы можете удалить объекты, которые вы собрали.
источник
Как насчет замены элементов, которые вы хотите удалить, на «n'th element», «n-1» и т. Д.?
Когда вы закончите, вы измените размер массива на «предыдущий размер - количество свопов»
источник
Если все объекты в вашем массиве уникальны или вы хотите удалить все вхождения объекта при обнаружении, вы можете быстро перечислить копию массива и использовать [NSMutableArray removeObject:], чтобы удалить объект из оригинала.
источник
+arrayWithArray
время выполнения?Ответ Бензадо выше - это то, что вы должны сделать для преформирования. В одном из моих приложений метод removeObjectsInArray занял 1 минуту, просто добавление в новый массив заняло 0,023 секунды.
источник
Я определяю категорию, которая позволяет мне фильтровать, используя блок, например так:
который затем можно использовать так:
источник
Более хорошей реализацией может быть использование метода категории ниже в NSMutableArray.
Блок предикатов может быть реализован для обработки каждого объекта в массиве. Если предикат возвращает true, объект удаляется.
Пример массива дат для удаления всех дат, которые были в прошлом:
источник
Итерации в обратном направлении были моими любимыми годами, но долгое время я никогда не сталкивался со случаем, когда самый глубокий (самый высокий счет) объект был удален первым. За мгновение до того, как указатель переходит к следующему индексу, ничего не происходит, и он падает.
Путь Бензадо - самый близкий к тому, что я делаю сейчас, но я никогда не думал, что после каждого удаления будет перестановка стека.
под Xcode 6 это работает
источник