原文:http://rypress.com/tutorials/objective-c/data-types/nsset

NSSet

NSSetNSArray, and NSDictionary are the three core collection classes defined by the Foundation Framework. An NSSet

NSSetNSArray, 和 NSDictionary是Foundation框架中三个主要集合类。一个NSSet对象是一个静态的无序的对象集合。Sets本身就是一个描述成员的最好单词,所以当回答诸如“这个对象是否是集合的成员的时候”,你可以使用set而不是数组去回答。

The basic collection classes of the Foundation Framework

集合可以存储OC的对象,对于基本C的数据类型来说必须将int之类的类型包装进NSnumber在存入set,array或者字典。

NSSet是不可变的,所以你不能在NSSet创建后增加或者删除元素。但是你可以对存储在其内部的对象进行修改。例如如果存储了一个叫做

NSMutableString对象,你可以对此对象发送setString等消息,这种现象也存在于NSMutableSet 和 NSCountedSet.

Creating Sets

一个NSSet可以通过setWithObjects:对象创建,他接受一个nil为结尾的对象列表。大多数例子都是使用NSString来解释,但是NSSet可以存储任何类型的OC对象,并且不需要为相同类型。

NSSet *americanMakes = [NSSet setWithObjects:@"Chrysler", @"Ford",
                                             @"General Motors", nil];
NSLog(@"%@", americanMakes);

NSSet也可以使用setWithArray: 这个方法可以将NSArray数组转化成NSSet。记住sets中存储的元素都是唯一的,所以也可以使用这个特性进行去重。例如:

NSArray *japaneseMakes = @[@"Honda", @"Mazda",
                           @"Mitsubishi", @"Honda"];
NSSet *uniqueMakes = [NSSet setWithArray:japaneseMakes];
NSLog(@"%@", uniqueMakes);    // Honda, Mazda, Mitsubishi

Sets对于存在其内部的袁术是强引用的。也就是说,一定要特别注意循环引用的发生,元素千万不要有对set本身的强引用。

枚举 Sets

快速枚举是对于set中元素进行迭代的首选方法,count方法计算set中所有元素个数。例如:

NSSet *models = [NSSet setWithObjects:@"Civic", @"Accord",
                                      @"Odyssey", @"Pilot", @"Fit", nil];
NSLog(@"The set has %li elements", [models count]);
for (id item in models) {
    NSLog(@"%@", item);
}

如果你对基于块的方式感兴趣,那么你可以使用enumerateObjectsUsingBlock:去对set进行枚举。这个方法只有一个参数就是^(id obj, BOOL *stop)。obj 是当前 object, *stop指针可以设定当前迭代的退出,就像这样:

[models enumerateObjectsUsingBlock:^(id obj, BOOL *stop) {
    NSLog(@"Current item: %@", obj);
    if ([obj isEqualToString:@"Fit"]) {
        NSLog(@"I was looking for a Honda Fit, and I found it!");
        *stop = YES;    // Stop enumerating items
    }
}];

上述代码中当迭代至@“Fit”元素的时候退出迭代,这就好比break语句一样。

注意既然set是武勋的,所以不能将其顺序作为外部依赖,NSSet不支持下标的方式进行访问元素。这也真是它和数组以及字段的一个主要区别。

Sets比较

处于与相等比较外,两个NSSet对象也可以其他比较,详见:

NSSet *japaneseMakes = [NSSet setWithObjects:@"Honda", @"Nissan",
                              @"Mitsubishi", @"Toyota", nil];
NSSet *johnsFavoriteMakes = [NSSet setWithObjects:@"Honda", nil];
NSSet *marysFavoriteMakes = [NSSet setWithObjects:@"Toyota",
                                                  @"Alfa Romeo", nil];

if ([johnsFavoriteMakes isEqualToSet:japaneseMakes]) {
    NSLog(@"John likes all the Japanese auto makers and no others");
}
if ([johnsFavoriteMakes intersectsSet:japaneseMakes]) {
    // You'll see this message
    NSLog(@"John likes at least one Japanese auto maker");
}
if ([johnsFavoriteMakes isSubsetOfSet:japaneseMakes]) {
    // And this one, too
    NSLog(@"All of the auto makers that John likes are Japanese");
}
if ([marysFavoriteMakes isSubsetOfSet:japaneseMakes]) {
    NSLog(@"All of the auto makers that Mary likes are Japanese");
}

成员检查

就像Foundation框架中其他集合一样,可以检查一个对象是否在一个set中。containsObject:方法返回BOOL来指示当前对象是否属于set。还有一个选择就是member:,当对象存在于set中的时候这个方法返回一个对象引用否则返回nil。

NSSet *selectedMakes = [NSSet setWithObjects:@"Maserati",
                                             @"Porsche", nil];
// BOOL checking
if ([selectedMakes containsObject:@"Maserati"]) {
    NSLog(@"The user seems to like expensive cars");
}
// nil checking
NSString *result = [selectedMakes member:@"Maserati"];
if (result != nil) {
    NSLog(@"%@ is one of the selected makes", result);
}

过滤set

你可以使用objectsPassingTest:方法对set进行内容的过滤,这个方法接受一个块,这个块在每一个元素上都会被执行。这个块中返回YES的时候表示当前元素被包含进新的set中,如果是NO则排除该元素。下面的代码表示找出所有开头为C的元素。

NSSet *toyotaModels = [NSSet setWithObjects:@"Corolla", @"Sienna",
                       @"Camry", @"Prius",
                       @"Highlander", @"Sequoia", nil];
NSSet *cModels = [toyotaModels objectsPassingTest:^BOOL(id obj, BOOL *stop) {
    if ([obj hasPrefix:@"C"]) {
        return YES;
    } else {
        return NO;
    }
}];
NSLog(@"%@", cModels);    // Corolla, Camry

因为NSSet是不可变的,所以objectsPassingTest:方法将会创建新的NSSet对象去包含过滤后的元素。这是很多不可变类所共有的特点,但是就元素而言他们并没有被拷贝而是一种引用,也就是说要他们是指向相同的内存的。

Sets组合

Sets可以使用 setByAddingObjectsFromSet:方法进行组合。因为Set的元素具有唯一性,所以在组合过程中也将去重。

NSSet *affordableMakes = [NSSet setWithObjects:@"Ford", @"Honda",
                                     @"Nissan", @"Toyota", nil];
NSSet *fancyMakes = [NSSet setWithObjects:@"Ferrari", @"Maserati",
                                          @"Porsche", nil];
NSSet *allMakes = [affordableMakes setByAddingObjectsFromSet:fancyMakes];
NSLog(@"%@", allMakes);

NSMutableSet

可变Sets允许你动态地增加和删除元素,这可比不可变Set灵活多了。除了成员检查,在动态的增删效率上也要快于NSMutableArray。

创建 Mutable Sets

可变Sets的创建方式和不可变Sets的创建方式雷同。或者你可以通过 setWithCapacity:创建一个空的Set。参数是定义了初始化的容量,但是这个容量是会被自动扩展的。

NSMutableSet *brokenCars = [NSMutableSet setWithObjects:
                            @"Honda Civic", @"Nissan Versa", nil];
NSMutableSet *repairedCars = [NSMutableSet setWithCapacity:];

增删对象

addObject: 和 removeObject:是NSMutableSet提供的主要特色方法。注意当被增加对象已经在Set中存在的时候将什么也不做。

NSMutableSet *brokenCars = [NSMutableSet setWithObjects:
                            @"Honda Civic", @"Nissan Versa", nil];
NSMutableSet *repairedCars = [NSMutableSet setWithCapacity:];
// "Fix" the Honda Civic
[brokenCars removeObject:@"Honda Civic"];
[repairedCars addObject:@"Honda Civic"];

NSLog(@"Broken cars: %@", brokenCars);     // Nissan Versa
NSLog(@"Repaired cars: %@", repairedCars); // Honda Civic

Filtering With Predicates

在可变Set中并没有objectsPassingTest:方法,但是你可以使用filterUsingPredicate:方法去实现相同的功能。Predicates的概念可能超出了本教程的范围,但是指向说这个东西足可以让查找和过滤变的很简单。幸运的是,NSPredicate类可以使用块进行初始化,从而我们不需要台关心它的格式化语法。

下面的代码片段是可变的基于predicate版本的Sets的过滤方法。再次强调,由于是可变数组,所以它都是在已有Set上进行操作的。

NSMutableSet *toyotaModels = [NSMutableSet setWithObjects:@"Corolla", @"Sienna",
                              @"Camry", @"Prius",
                              @"Highlander", @"Sequoia", nil];
NSPredicate *startsWithC = [NSPredicate predicateWithBlock:
                            ^BOOL(id evaluatedObject, NSDictionary *bindings) {
                                if ([evaluatedObject hasPrefix:@"C"]) {
                                    return YES;
                                } else {
                                    return NO;
                                }
                            }];
[toyotaModels filterUsingPredicate:startsWithC];
NSLog(@"%@", toyotaModels);    // Corolla, Camry

Set 的集合操作

NSMutableSet也提供了一些基本的Set集合的操作方法。如下:

NSSet *japaneseMakes = [NSSet setWithObjects:@"Honda", @"Nissan",
                        @"Mitsubishi", @"Toyota", nil];
NSSet *johnsFavoriteMakes = [NSSet setWithObjects:@"Honda", nil];
NSSet *marysFavoriteMakes = [NSSet setWithObjects:@"Toyota",
                             @"Alfa Romeo", nil];

NSMutableSet *result = [NSMutableSet setWithCapacity:];
// Union
[result setSet:johnsFavoriteMakes];
[result unionSet:marysFavoriteMakes];
NSLog(@"Either John's or Mary's favorites: %@", result);
// Intersection
[result setSet:johnsFavoriteMakes];
[result intersectSet:japaneseMakes];
NSLog(@"John's favorite Japanese makes: %@", result);
// Relative Complement
[result setSet:japaneseMakes];
[result minusSet:johnsFavoriteMakes];
NSLog(@"Japanese makes that are not John's favorites: %@",
      result);

条件枚举

对可变Set的迭代和对不可变Set的迭代原理上都一言,但是这里有一个忠告,在迭代过程中千万不要去改变Set内容。

下面的例子中间的for in循环的迭代是错误的。因为它在迭代过程中删除了元素。

// DO NOT DO THIS. EVER.
NSMutableSet *makes = [NSMutableSet setWithObjects:@"Ford", @"Honda",
                       @"Nissan", @"Toyota", nil];
for (NSString *make in makes) {
    NSLog(@"%@", make);
    if ([make hasPrefix:@"T"]) {
        // Throws an NSGenericException:
        // "Collection was mutated while being enumerated"
        [makes removeObject:@"Toyota"];
    }
}

最查档的做法如下,通过一个临时的拷贝来过滤出目标元素,然后在远Set中删除对应的元素。

NSMutableSet *makes = [NSMutableSet setWithObjects:@"Ford", @"Honda",
                       @"Nissan", @"Toyota", nil];
NSArray *snapshot = [makes allObjects];
for (NSString *make in snapshot) {
    NSLog(@"%@", make);
    if ([make hasPrefix:@"T"]) {
        [makes removeObject:@"Toyota"];
    }
}
NSLog(@"%@", makes);

NSCountedSet

NSCountedSet类非常值得介绍一下。他是NSMutableSet的子类,它能够记录某一个元素被的个数。内部维护了一个高效的对象计数器。

NSCountedSet和mutable之间的主要差别是countForObject:方法。这个方法也可以替换containsObject:方法。例如:

NSCountedSet *inventory = [NSCountedSet setWithCapacity:];
[inventory addObject:@"Honda Accord"];
[inventory addObject:@"Honda Accord"];
[inventory addObject:@"Nissan Altima"];
NSLog(@"There are %li Accords in stock and %li Altima",
      [inventory countForObject:
      [inventory countForObject:

Please see the official documentation for more details.

12-5 NSSet的更多相关文章

  1. Xcode 7.0 SDK(Software Development Kit) 及 Sandbox(沙盒) 存放路径

    1. Sandbox(沙盒) 存放路径 我的硬盘/Users/wj121/Library/Developer/CoreSimulator/Devices/879D7E35-BE50-4620-97E1 ...

  2. python 各模块

    01 关于本书 02 代码约定 03 关于例子 04 如何联系我们 1 核心模块 11 介绍 111 内建函数和异常 112 操作系统接口模块 113 类型支持模块 114 正则表达式 115 语言支 ...

  3. Python Standard Library

    Python Standard Library "We'd like to pretend that 'Fredrik' is a role, but even hundreds of vo ...

  4. 在mybatis中写sql语句的一些体会

    本文会使用一个案例,就mybatis的一些基础语法进行讲解.案例中使用到的数据库表和对象如下: article表:这个表存放的是文章的基础信息 -- ------------------------- ...

  5. Fouandation(NSString ,NSArray,NSDictionary,NSSet) 中常见的理解错误区

    Fouandation 中常见的理解错误区 1.NSString //快速创建(实例和类方法) 存放的地址是 常量区 NSString * string1 = [NSString alloc]init ...

  6. 一些NSArray,NSDictionary,NSSet相关的算法知识

    iOS编程当中的几个集合类:NSArray,NSDictionary,NSSet以及对应的Mutable版本,应该所有人都用过.只是简单使用的话,相信没人会用错,但要做到高效(时间复杂度)精确(业务准 ...

  7. IOS集合NSSet与NSMutableSet知识点

    NSSet在实际应用中与NSArray区别不大,但是如果你希望查找NSArray中的某一个元素,则需要遍历整个数组,效率低下.而NSSet在查找某一特定的元素的时候则是根据hash算法直接找到此元素的 ...

  8. iOS - OC NSSet 集合

    前言 NSSet:集合 @interface NSSet<__covariant ObjectType> : NSObject <NSCopying, NSMutableCopyin ...

  9. 【IOS 开发】Objective-C Foundation 框架 -- 字符串 | 日期 | 对象复制 | NSArray | NSSet | NSDictionary | 谓词

    一. 字符串 API 1. NSString 用法简介 (1) NSString API 介绍 NSString 功能 : -- 创建字符串 : 使用 init 开头的实例方法, 也可以使用 Stri ...

  10. NSDictionary NSMutableDictionary NSSet NSMutableSet

    //description只是返回了一个字符串 //    [person description]; //        //如果想要打印需要NSLog //    NSLog(@"%@& ...

随机推荐

  1. html dom与javascript的关系 -我们用JavaScript对网页(HTML)进行的所有操作都是通过DOM进行的

    一,什么是DOM (参考源http://www.cnblogs.com/chaogex/p/3959723.html) DOM是什么 DOM全称为The Document Object Model,应 ...

  2. SocketServer-实现并发处理3

    用socketserver创建一个服务的步骤: 1  创建一个request handler class(请求处理类),合理选择StreamRequestHandler和DatagramRequest ...

  3. springboot2.0 如何异步操作,@Async失效,无法进入异步

    springboot异步操作可以使用@EnableAsync和@Async两个注解,本质就是多线程和动态代理. 一.配置一个线程池 @Configuration @EnableAsync//开启异步 ...

  4. hdu1950 Bridging signals

    LIS nlogn的时间复杂度,之前没有写过. 思路是d[i]保存长度为i的单调不下降子序列末尾的最小值. 更新时候,如果a[i]>d[len],(len为目前最长的单调不下降子序列) d[++ ...

  5. HDOJ.1009 FatMouse' Trade (贪心)

    FatMouse' Trade 点我挑战题目 题意分析 每组数据,给出有的猫粮m与房间数n,接着有n行,分别是这个房间存放的食物和所需要的猫粮.求这组数据能保证的最大的食物是多少? (可以不完全保证这 ...

  6. bzoj1656: [Usaco2006 Jan] The Grove 树木 (bfs+新姿势)

      题目大意:一个n*m的图中,“.”可走,“X”不可走,“*”为起点,问从起点开始绕所有X一圈回到起点最少需要走多少步. 一开始看到这题,自己脑洞了下怎么写,应该是可过,然后跑去看了题解,又学会了一 ...

  7. 【初级算法】2.买卖股票的最佳时机 II

    题目: 给定一个数组,它的第 i 个元素是一支给定股票第 i 天的价格. 设计一个算法来计算你所能获取的最大利润.你可以尽可能地完成更多的交易(多次买卖一支股票). 注意:你不能同时参与多笔交易(你必 ...

  8. Tomcat启动时报org.springframework.web.context.ContextLoaderListener错误解决方案

    问题现象:新从svn上检出maven的项目maven+spring+springmvc项目在Tomcat启动时,报如下错误. 严重: Error configuring application lis ...

  9. 《时间序列分析及应用:R语言》读书笔记--第一章 引论

    "春节假期是难得的读书充电的时间."--来自某boss.假期能写多少算多少,一个是题目中的这本书,另一个是<python核心编程>中的高级部分,再一个是拖着的<算 ...

  10. Blender绘制大脑表层,并高亮染色

    首先, 有必要熟悉一下Blender的一些快捷键.(实在不想吐槽Blender反人类的交互操作了) 按鼠标右键是选择某个物体.(是右键,而不是左键!) 按A键,取消选中或者选中全部物体. 按H键,隐藏 ...