Когда лучше использовать NSSet вместо NSArray?


Ответы:


173

Когда порядок элементов в коллекции не важен, наборы обеспечивают лучшую производительность для поиска элементов в коллекции.

Причина в том, что набор использует хеш-значения для поиска элементов (например, словарь), в то время как массив должен перебирать все свое содержимое, чтобы найти конкретный объект.


9
log (1) vs log (n)
rohan-patel

25
@ rohan-patel Правильно - O (1) vs O (n)
Мансуров Руслан

1
Если заказано: O (1) против O (logn), так как возможен двоичный поиск. Если неупорядочено, то O (1) vs O (n)
баскинеминенс

180

Изображение из документации Apple очень хорошо описывает это:

Коллекции Objective-C

Arrayприведен упорядоченный (порядок сохраняется при добавлении) последовательность элементов

[array addObject:@1];
[array addObject:@2];
[array addObject:@3];
[array addObject:@4];
[array addObject:@6];
[array addObject:@4];
[array addObject:@1];
[array addObject:@2];

[1, 2, 3, 4, 6, 4, 1, 2]

Setэто отдельный (без дубликатов) неупорядоченный список элементов

[set addObject:@1];
[set addObject:@2];
[set addObject:@3];
[set addObject:@4];
[set addObject:@6];
[set addObject:@4];
[set addObject:@1];
[set addObject:@2];

[1, 2, 6, 4, 3]

2
В вашем примере вы добавляете примитивы в массив и набор. Ни то, ни другое невозможно, потому что они могут содержать только объекты.
FreeAsInBeer

10
Спасибо за правку @Zaheer, но на самом деле она недействительна. Я не добавлял примитивов. Я добавлял литералы.
Джеймс Вебстер,

Отличное объяснение :) +1 :)
Карун

1
Люблю ваше объяснение! +1 за это
бунт

66

Лучший ответ - собственная документация Apple .

введите описание изображения здесь

Основное различие заключается в том, что оно NSArrayпредназначено для упорядоченной коллекции и NSSetдля неупорядоченной коллекции.

Есть несколько статей, в которых говорится о разнице в скорости между ними, как эта . Если вы перебираете неупорядоченную коллекцию, NSSetотлично. Однако во многих случаях вам нужно делать то, что только NSArrayможет, поэтому вы жертвуете скоростью ради этих способностей.

NSSet

  • Доступ к элементам в первую очередь путем сравнения
  • Неупорядоченный
  • Не допускает дубликатов

NSArray

  • Доступ к элементам по индексу
  • Приказал
  • Допускает дубликаты

Вот и все, что нужно сделать! Сообщите мне, если это поможет.


Не обязательно всегда жертвовать NSSetради индексации. Обычно для одних и тех же данных используются две разные структуры данных. Или вы создаете и индексируете этот массив :) Но тогда лучше использовать БД, в которой он уже реализован.
Sulthan

«Создайте индекс для этого массива». Вы не можете создать индекс для NSSet. Вы можете использовать множество различных техник. И если вам нужно пожертвовать ОБЕИ памятью и вычислительной мощностью, то вы делаете это неправильно.
Sulthan

Поскольку вопрос касается NSSetи NSArray, мой ответ точный и полный. Да, вы можете создавать другие структуры данных, но я просто сравниваю эти две.
woz

Я проголосовал за, но ваш ответ неверен, когда вы говорите о жертвах. Если вам нужна какая-то функциональность NSArrayи некоторая функциональность NSSet, правильный ответ - не «использовать NSArrayи жертвовать производительностью». Ответ - объединить оба или использовать другую структуру данных.
Sulthan

Я бы сказал, что основное различие установлено для уникальных объектов, где массив может иметь дубликаты. Аспект порядка вторичен по отношению к этому факту.
Malhal

12

NSOrderedSet доступен в iOS 5+, поэтому основное различие заключается в том, хотите ли вы дублировать объекты в структуре данных.


9

NSArray :

  1. Упорядоченный сбор данных
  2. Допускает дубликаты
  3. Это объект коллекционного типа

NSSet :

  1. Неупорядоченный сбор данных
  2. Не допускает дубликатов
  3. Это также объект коллекционного типа

7

Массив используется для доступа к элементам по их индексу. Любой элемент можно вставить в массив несколько раз. Массивы сохраняют порядок своих элементов.

Набор используется в основном только для проверки, находится ли элемент в коллекции или нет. Элементы не имеют понятия порядка или индексации. Вы не можете иметь предмет в наборе дважды.

Если массив хочет проверить, содержит ли он элемент, он должен проверить все его элементы. Наборы предназначены для использования более быстрых алгоритмов.

Вы можете представить себе набор как словарь без значений.

Обратите внимание, что массив и набор - не единственные структуры данных. Есть и другие, например, очередь, стек, куча, куча Фибоначчи. Я бы рекомендовал прочитать книгу об алгоритмах и структурах данных.

См. Википедию для получения дополнительной информации.


Фактически, массив нужно проверять только до того момента, когда элемент найден. Если элемент ЕСТЬ в массиве, очень редко нужно будет проверять каждый элемент.
FreeAsInBeer

Да, как вы говорите, «если элемент ЕСТЬ в массиве». Если вы этого ожидаете, вам не нужно проверять, есть он или нет. Сложность containsоперации O(n). Количество сравнений, когда нет в массиве n. Среднее количество сравнений, когда объект находится в массиве, составляет n/2. Даже если объект найден, производительность ужасная.
Sulthan

Производительность была бы ужасной с большим массивом. Если бы вы знали, что массив может стать довольно большим, тогда есть способы улучшить его производительность, например, используя массив массивов.
FreeAsInBeer

Если операция равенства стоит дорого, вы увидите разницу даже для массивов из 3 элементов. И то же поведение, что и при большом массиве, может произойти, если вы повторяете операцию много раз (например, используйте операцию в цикле for). Вы слышали об амортизированной сложности? Сложность все еще линейна, а производительность ужасна по сравнению с набором (обычно с постоянной сложностью).
Sulthan

Очевидно, разница будет, я просто утверждаю, что нотация большого o является экспоненциальной; с небольшими массивами разница будет мизерной. Кроме того, у NSArrays есть другие преимущества в скорости перед NSSets. Как всегда, это компромисс.
FreeAsInBeer

5
NSArray *Arr;
NSSet *Nset;

Arr=[NSArray arrayWithObjects:@"1",@"2",@"3",@"4",@"2",@"1", nil];
Nset=[NSSet setWithObjects:@"1",@"2",@"3",@"3",@"5",@"5", nil];

NSLog(@"%@",Arr);
NSLog(@"%@",Nset);

массив

2015-12-04 11: 05: 40.935 [598: 15730] (1, 2, 3, 4, 2, 1)

набор

2015-12-04, 11: 05: 43.362 [598: 15730] {(3, 1, 2, 5)}


4

Основные отличия уже были приведены в других ответах.

Я просто хотел бы отметить, что из-за способа реализации наборов и словарей (т. Е. С использованием хэшей) следует быть осторожным, чтобы не использовать изменяемые объекты для ключей.

Если ключ изменен, то хеш (возможно) тоже изменится, указывая на другой индекс / сегмент в хеш-таблице. Исходное значение не будет удалено и будет фактически учтено при перечислении или запросе структуры для ее размера / количества.

Это может привести к тому, что некоторые ошибки действительно сложно будет найти.


3

Здесь вы можете найти довольно подробное сравнение структур данных NSArrayи NSSet.

Краткие выводы:

Да, NSArray быстрее, чем NSSet, потому что просто удерживает и повторяет. Всего на 50% быстрее для построения и на 500% быстрее для итераций. Урок: если вам нужно только перебрать содержимое, не используйте NSSet.

Конечно, если вам нужно протестировать на включение, старайтесь избегать NSArray. Даже если вам нужно как итерационное, так и инклюзивное тестирование, вы, вероятно, все равно должны выбрать NSSet. Если вам нужно упорядочить свою коллекцию, а также проверить ее на включение, вам следует рассмотреть возможность сохранения двух коллекций (NSArray и NSSet), каждая из которых содержит одни и те же объекты.

NSDictionary создается медленнее, чем NSMapTable, поскольку ему необходимо копировать ключевые данные. Это компенсируется более быстрым поиском. Конечно, у них разные возможности, поэтому в большинстве случаев это определение следует делать по другим факторам.


Хотя теоретически это может дать ответ на вопрос, было бы предпочтительнее включить сюда основные части ответа и предоставить ссылку для справки.
Tunaki

2

Обычно вы используете Set, когда скорость доступа имеет существенное значение, а порядок не имеет значения или определяется другими способами (через предикат или дескриптор сортировки). Core Data, например, использует наборы, когда доступ к управляемым объектам осуществляется через отношение ко многим.


1

Просто чтобы добавить немного, я иногда использую set, чтобы удалить дубликаты из массива, например: -

NSMutableSet *set=[[NSMutableSet alloc]initWithArray:duplicateValueArray]; // will remove all the duplicate values
Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.