何时使用NSSet比NSArray更好?

114

我在我的应用程序中多次使用了 NSSet,但从未自己创建过。

何时最好使用 NSSet 而不是 NSArray,为什么?

11个回答

183

这张来自苹果官方文档的图片描述得非常清楚:

Objective-C Collections

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,但实际上它是无效的。我要添加的不是原语,而是字面值。 - James Webster
对于有序且无重复数据的集合,您可以使用iOS 5及更高版本中已经引入的NSOrderedSet。 - Pradeep Singh

174

当集合中的项目顺序不重要时,使用集合可以更快地查找项目。

原因是集合使用哈希值来查找项目(类似于字典),而数组必须遍历整个内容才能找到特定的对象。


25
正确的是O(1)与O(n)的区别。 - Ruslan Mansurov
1
如果是有序的:O(1) vs O(logn),因为可以使用二分查找。如果是无序的,则为O(1) vs O(n)。 - baskInEminence

67
最好的答案是苹果自己的文档

enter image description here

NSArray是用于有序集合的,而NSSet是用于无序集合的。两者之间的主要区别在于排序。
有一些文章讨论了它们之间速度差异,比如这篇。如果你正在遍历无序集合,NSSet非常好。然而,在许多情况下,你需要执行只有NSArray能够完成的任务,因此,你会为这些功能而牺牲速度。 NSSet
  • 通过比较来访问项目
  • 无序
  • 不允许重复项
NSArray
  • 可以通过索引访问项目
  • 有序
  • 允许重复项
以上就是它们之间的区别!如果有帮助,请告诉我。

你不必总是为了索引而牺牲 NSSet。使用两种不同的数据结构来处理相同的数据是很常见的。或者你可以在数组上建立索引 :) 但是这时最好使用已经实现了它的数据库。 - Sulthan
在该数组上建立索引。你不能在 NSSet 上建立索引。有许多不同的技术可以使用。如果您需要在内存和处理能力方面做出牺牲,那么您就错了。 - Sulthan
由于问题涉及到 NSSetNSArray,我的回答是准确和完整的。是的,你可以构建其他数据结构,但我只是在比较这两个。 - woz
我已经点赞了,但是你在谈到牺牲时的回答是不正确的。如果你需要从NSArray获取一些功能,同时又需要从NSSet获取一些功能,正确的答案不是“使用NSArray并牺牲性能”。正确的答案是将两者结合起来或使用不同的数据结构。 - Sulthan
我会说主要的区别在于set是用于唯一对象,而数组可以有重复项。排序方面是次要的。 - malhal
“如果你正在遍历一个无序集合,使用 NSSet 是很好的选择。”这是不准确的。根据你提供的文章,无论集合是否有序,NSArray 在迭代时都更有效率:“教训是:如果你只需要遍历内容,请不要使用 NSSet。”当你需要进行大量查找时,NSSet 应该是首选的数据结构。 - Ilya Vinogradov

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符号是指数级别的;对于小数组来说,差异微乎其微。此外,NSArrayNSSet具有其他速度优势。像往常一样,这是一个权衡取舍。 - 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

这里你可以找到一个相当详细的NSArrayNSSet数据结构的比较。

简短的结论:

是的,对于简单地持有和迭代,NSArrayNSSet更快。构造速度最多快50%,迭代速度最多快500%。教训:如果您只需要迭代内容,请不要使用NSSet

当然,如果您需要进行包含测试,则应避免使用NSArray。即使您需要迭代和包含测试,您仍然应该选择NSSet。如果您需要保持集合有序并且还需要进行包含测试,则应考虑保留两个集合(一个NSArray和一个NSSet),每个集合都包含相同的对象。

NSDictionary的构造速度比NSMapTable慢,因为它需要复制键数据。但是,由于它查找速度更快,所以它弥补了这一点。当然,两者具有不同的功能,因此大多数情况下,这种决定应基于其他因素。


虽然这理论上回答了问题,但最好在此处包含答案的基本部分,并提供参考链接。 - Tunaki

2

当访问速度至关重要且顺序无关紧要,或者由其他方式(通过谓词或排序描述符)确定顺序时,通常会使用Set。例如,Core Data在通过“一对多”关系访问托管对象时使用集合。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接