Да, использование NSSet - разумный подход.
Чтобы добавить ответ Джима Пулса, вот альтернативный подход к удалению дубликатов при сохранении порядка:
// Initialise a new, empty mutable array
NSMutableArray *unique = [NSMutableArray array];
for (id obj in originalArray) {
if (![unique containsObject:obj]) {
[unique addObject:obj];
}
}
По сути, это тот же подход, что и у Джима, но копирование уникальных элементов в новый изменяемый массив вместо удаления дубликатов из оригинала. Это делает его немного более эффективным в отношении памяти в случае большого массива с большим количеством дубликатов (не нужно делать копию всего массива) и, на мой взгляд, немного более читабельным.
Обратите внимание, что в любом случае проверка на предмет того, включен ли уже элемент в целевой массив (с использованием containsObject:
в моем примере или indexOfObject:inRange:
в Jim's), плохо масштабируется для больших массивов. Эти проверки выполняются за время O (N), что означает, что если вы удвоите размер исходного массива, то каждая проверка будет выполняться вдвое дольше. Поскольку вы выполняете проверку для каждого объекта в массиве, вы также будете запускать больше таких более дорогих проверок. Общий алгоритм (как мой, так и Джима) выполняется за время O (N 2 ), что быстро растет с ростом исходного массива.
Чтобы сократить это время до O (N), вы можете использовать NSMutableSet
для хранения записи элементов, уже добавленных в новый массив, поскольку NSSet ищет O (1), а не O (N). Другими словами, проверка того, является ли элемент членом NSSet, занимает одно и то же время, независимо от того, сколько элементов в наборе.
Код, использующий этот подход, будет выглядеть примерно так:
NSMutableArray *unique = [NSMutableArray array];
NSMutableSet *seen = [NSMutableSet set];
for (id obj in originalArray) {
if (![seen containsObject:obj]) {
[unique addObject:obj];
[seen addObject:obj];
}
}
Это все еще кажется немного расточительным; мы все еще генерируем новый массив, когда вопрос прояснил, что исходный массив является изменяемым, поэтому мы должны иметь возможность его дедупликации и сэкономить память. Примерно так:
NSMutableSet *seen = [NSMutableSet set];
NSUInteger i = 0;
while (i < [originalArray count]) {
id obj = [originalArray objectAtIndex:i];
if ([seen containsObject:obj]) {
[originalArray removeObjectAtIndex:i];
// NB: we *don't* increment i here; since
// we've removed the object previously at
// index i, [originalArray objectAtIndex:i]
// now points to the next object in the array.
} else {
[seen addObject:obj];
i++;
}
}
ОБНОВЛЕНИЕ : Юрий Ниязов указал , что мой последний ответ на самом деле работает на O (N 2 ), потому что removeObjectAtIndex:
, вероятно, работает на O (N) время.
(Он говорит «вероятно», потому что мы не знаем наверняка, как он реализован; но одна из возможных реализаций состоит в том, что после удаления объекта по индексу X метод затем проходит по каждому элементу из индекса X + 1 до последний объект в массиве, перемещая их к предыдущему индексу. Если это так, то это действительно производительность O (N).)
Итак, что делать? Это зависит от ситуации. Если у вас большой массив и вы ожидаете только небольшое количество дубликатов, то дедупликация на месте будет работать нормально и избавит вас от необходимости создавать дублирующий массив. Если у вас есть массив, в котором вы ожидаете много дубликатов, то, вероятно, лучше всего создать отдельный дедуплицированный массив. Вывод здесь заключается в том, что нотация big-O описывает только характеристики алгоритма, но не может сказать вам окончательно, что лучше для любого конкретного обстоятельства.