原文: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. [JSOI2010]部落划分 最小生成树

    一道最小生成树经典题 由于是最靠近的两个部落尽可能远,如果我们先处理出任意两个居住点之间的距离并将其当做边,那么我们可以发现,因为在一个部落里面的边是不用计入答案的,所以应该要尽量把小边放在一个部落里 ...

  2. BZOJ [Ctsc2002] Award 颁奖典礼 解题报告

    [Ctsc2002] Award 颁奖典礼 Description IOI2002的颁奖典礼将在YONG-IN Hall隆重举行.人们在经历了充满梦幻的世界杯之后变得更加富于情趣.为了使颁奖典礼更具魅 ...

  3. UVA.136 Ugly Numbers (优先队列)

    UVA.136 Ugly Numbers (优先队列) 题意分析 如果一个数字是2,3,5的倍数,那么他就叫做丑数,规定1也是丑数,现在求解第1500个丑数是多少. 既然某数字2,3,5倍均是丑数,且 ...

  4. redis中如何对 key 进行分类

    因为redis中的 hash是不支持设置过期时间的,如果我们要 设置过期时间,还要分类存储,可以用下面折中的方法 其实就是我们把 key 定义的有规律一些,通过在key的字符串内部 分类,上图只是因为 ...

  5. noip模拟赛 helloworld

    分析:对于第一个点,答案为26^n - 25^n,这个很好想.另外30%的点因为n <= 5,所以可以直接暴力搜索. 数学方法不是很好处理,考虑dp,设f[i][j]为前i位匹配到危险串第j位的 ...

  6. 题解【poj2774 Long Long Message】

    Description 求两个串的最长连续公共字串 Solution 后缀数组入门题吧 把两个串连在一起,中间加一个分隔符,然后跑一遍后缀数组,得到 height 和 sa 一个 height[i] ...

  7. win下删除oracle

    关掉oralce服务. 具体方式有两种: 1.命令行下使用命令关闭服务:net stop OracleServiceORCL ,ORCL是我的实例名字,换成你的) 2.运行services.msc到系 ...

  8. eclipse如何远程debug/断开远程debug

    eclipse如何远程debug? 当你的代码已经部署到生产或者测试环境的时候,你如何debug判断线上的问题呢? debug之前必须保证本地代码和远程代码完全一致,否则将不能建立连接 在eclips ...

  9. 课下加分项目 MYPWD 20155335 俞昆

    Mypwd 的解读与实现 20155335 linux下pwd命令的编写 实验要求: 1 .学习pwd命令 2 . 研究pwd实现需要的系统调用(man -k; grep),写出伪代码 3 .实现my ...

  10. mybatis 插入语句name no find

    1.可参考连接:https://www.cnblogs.com/thomas12112406/p/6217211.html 2.dao层的配置 void addUser(@Param("un ...