您的位置:首页 > 移动开发 > IOS开发

【IOS 开发学习总结-OC-30】★★OC之foundation 框架——集合(NSSet 与 NSMutableSet)

2015-10-04 09:20 831 查看
有关NSSet的概述介绍参见前面的一篇博文: 【IOS 开发学习总结-OC-28】★objective-c之foundation 框架——集合类的综述

NSSet的功能和用法

NSSet按 Hash 算法 来存储集合中的元素,因此具有很好的存取 和查找性能。

它与 数组NSArray 最大的不同是:元素没有索引。除此之外,它们有大量相似之处,它们在如下方面的调用机制都很 相似。

1. 都可通过 count 方法获取集合元素的数量。

2. 都可通过快速枚举来遍历集合元素。

3. 都提供了集合的所有的元素和部分元素进行 KVO 编程的方法。

4. 都提供了
valueForKey:
setValue:forKey:
方法对集合元素整体进行 KVC 编程 。

5. 通过 objectEnumerator方法获取NSEnumerator枚举器对集合元素进行遍历。对 NSSet 提供反向迭代器没有意义——NSSet 集合本身就是无序的。

6. 都提供了
makeObjectsPerformSelector:
makeObjectsPerformSelector:withObject:
方法对集合元素整体调用某个方法,以及
enumerateObjectsUsingBlock:
enumerateObjectsWithOptions:usingBlock:
对集合整体或部分元素迭代执行代码块。

NSSet的基本用法

NSSet同样提供了类方法和实例方法来初始化NSSet集合,其中,以 set 开头的为类方法,以 init 开头的方法为实例方法。

NSSet常用的方法:

1.
setByAddingObject:
向集合中添加一个新元素 ,返回添加元素后的新集合。

2.
setByAddingObjectsFromSet:
使用NSSet集合向集合中添加多个元素,返回添加元素后的新集合。

3.
setByAddingObjectsFromArray:
使用 NSArray 集合向集合添加多个新元素,返回添加元素后的新集合。

4.
allObjects:
返回该集合中所有元素组成的 NSArray.

5.
anyObject:
返回该集合中的某个元素。——该方法返回的元素是不确定的,但该方法并不保证随机返回集合元素。

说明:



6.
containsObject:
判断集合是否包含指定元素

7.
member:
判断该集合是否包含与 该参数相等的元素。——如果包含,返回相等的元素,否则返nil.

8.
objectsPassingTest:
需要传入一个代码块对集合元素进行过滤,满足该代码块 条件的集合元素被保留下来并组成一个新的 NSSet 集合作为返回值。

9.
objectsWithOptions:PassingTest:
与前一方法类似,只是额外地传入一个 NSEnumerationOptions 迭代选项参数。

10.
isSubsetOfSet:
判断当前集合是否为另一个集合的子集。

11.
intersectsSet:
判断2个集合的元素是否有 相同的元素。——是否有交集。

12.
isEqualSet:
判断2个集合的元素是否相等。

示例程序:

#import <Foundation/Foundation.h>

// 定义一个函数,该函数可把NSArray或NSSet集合转换为字符串
NSString* NSCollectionToString(id collection)
{
NSMutableString* result = [NSMutableString
stringWithString:@"["];
// 使用快速枚举遍历NSSet集合
for(id obj in collection)
{
[result appendString:[obj description]];
[result appendString:@", "];
}
// 获取字符串长度
NSUInteger len = [result length];
// 去掉字符串最后的两个字符
[result deleteCharactersInRange:NSMakeRange(len - 2, 2)];
[result appendString:@"]"];
return result;
}
int main(int argc , char * argv[])
{
@autoreleasepool{
// 用4个元素初始化NSSet集合,
// 故意传入两个相等的元素, NSSet集合只会保留一个元素
NSSet* set1 = [NSSet setWithObjects:
@"疯狂iOS讲义" , @"疯狂Android讲义",
@"疯狂Ajax讲义" ,@"疯狂iOS讲义" , nil];
// 程序输出set1集合中元素个数为3
NSLog(@"set1集合中元素个数为%ld" , [set1 count]);
NSLog(@"s1集合:%@" , NSCollectionToString(set1));
NSSet* set2 = [NSSet setWithObjects:
@"孙悟空" , @"疯狂Android讲义",
@"猪八戒" , nil];
NSLog(@"s2集合:%@" , NSCollectionToString(set2));
// 向set1集合中添加单个元素,将添加元素后生成的新集合赋给set1
set1 = [set1 setByAddingObject:@"Struts 2.1权威指南"];
NSLog(@"添加一个元素后:%@" , NSCollectionToString(set1));
// 使用NSSet集合向set1集合中添加多个元素,相当计算两个集合的并集
NSSet* s = [set1 setByAddingObjectsFromSet:set2];
NSLog(@"set1与set2的并集:%@" , NSCollectionToString(s));
// 计算两个NSSet集合的是否有交集
BOOL b = [set1 intersectsSet:set2];
NSLog(@"set1与set2是否有交集:%d" , b);// 将输出代表YES的1
// 判断set2是否是set1的子集
BOOL bo = [set2 isSubsetOfSet:set1];
NSLog(@"set2是否为set1的子集:%d" , bo);// 将输出代表NO的0
// 判断NSSet集合是否包含指定元素
BOOL bb = [set1 containsObject:@"疯狂Ajax讲义"];
NSLog(@"set1是否包含\"疯狂Ajax讲义\":%d" , bb);// 将输出代表YES的1
// 下面两行代码将取出相同的元素,但取出哪个元素是不确定的。
NSLog(@"set1取出一个元素:%@"
, [set1 anyObject]);
NSLog(@"set1取出一个元素:%@"
, [set1 anyObject]);
// 使用代码块对集合元素进行过滤
NSSet* filteredSet = [set1 objectsPassingTest:
^(id obj, BOOL *stop)
{
return (BOOL)([obj length] > 8);
}];
NSLog(@"set1中元素的长度大于8的集合元素有:%@"
, NSCollectionToString(filteredSet));
}
}


编译运行结果:

2015-10-03 19:44:53.100 923[2729:148826] set1集合中元素个数为3
2015-10-03 19:44:53.103 923[2729:148826] s1集合:[疯狂Android讲义, 疯狂iOS讲义, 疯狂Ajax讲义]
2015-10-03 19:44:53.104 923[2729:148826] s2集合:[孙悟空, 疯狂Android讲义, 猪八戒]
2015-10-03 19:44:53.104 923[2729:148826] 添加一个元素后:[疯狂Android讲义, 疯狂iOS讲义, Struts 2.1权威指南, 疯狂Ajax讲义]
2015-10-03 19:44:53.104 923[2729:148826] set1与set2的并集:[疯狂iOS讲义, 猪八戒, 疯狂Ajax讲义, 疯狂Android讲义, 孙悟空, Struts 2.1权威指南]
2015-10-03 19:44:53.105 923[2729:148826] set1与set2是否有交集:1
2015-10-03 19:44:53.105 923[2729:148826] set2是否为set1的子集:0
2015-10-03 19:44:53.105 923[2729:148826] set1是否包含"疯狂Ajax讲义":1
2015-10-03 19:44:53.106 923[2729:148826] set1取出一个元素:疯狂Android讲义
2015-10-03 19:44:53.106 923[2729:148826] set1取出一个元素:疯狂Android讲义
2015-10-03 19:44:53.107 923[2729:148826] set1中元素的长度大于8的集合元素有:[疯狂Android讲义, Struts 2.1权威指南]


NSSet 判断集合元素重复的标准

HahSet集合判断2个元素相等的标准:

1. 2个对象通过 isEqual:方法比较返回 YES;

2. 2个对象的 hash 方法返回值也相等。

示例程序:

FKUser.h

#import <Foundation/Foundation.h>

@interface FKUser : NSObject
@property (nonatomic , copy) NSString* name;
@property (nonatomic , copy) NSString* pass;
- (id) initWithName:(NSString*) aName
pass:(NSString*) aPass;
- (void) say:(NSString*) content;
@end


FKUser.m

#import "FKUser.h"

@implementation FKUser
@synthesize name;
@synthesize pass;
- (id) initWithName:(NSString*) aName
pass:(NSString*) aPass
{
if(self = [super init])
{
name = aName;
pass = aPass;
}
return self;
}
- (void) say:(NSString*) content
{
NSLog(@"%@说:%@",self.name , content);
}
// 会重写isEqual:方法,重写该方法的比较标准是,
// 如果两个FKUser的name、pass相等,即可认为两个FKUser相等。
- (BOOL) isEqual:(id)other
{
if(self == other)
{
return YES;
}
if([other class] == FKUser.class)
{
FKUser* target = (FKUser*)other;
return [self.name isEqualToString:target.name]
&& [self.pass isEqualToString:target.pass];
}
return NO;
}
// 重写hash方法,重写该方法的比较标准是,
// 如果两个FKUser的name、pass相等,两个FKUser的hash方法返回值相等
- (NSUInteger) hash
{
NSLog(@"===hash===");
NSUInteger nameHash = name == nil ? 0 : [name hash];
NSUInteger passHash = pass == nil ? 0 : [pass hash];
return nameHash * 31 + passHash;
}
// 重写description方法,可以直接看到FKUser对象的状态
- (NSString*) description
{
return [NSString stringWithFormat:
@"<FKUser[name=%@, pass=%@]>"
, self.name , self.pass];
}
@end


NSSetTest2.m

#import <Foundation/Foundation.h>
#import "FKUser.h"

// 定义一个函数,该函数可把NSArray或NSSet集合转换为字符串
NSString* NSCollectionToString(id array)
{
NSMutableString* result = [NSMutableString
stringWithString:@"["];
for(id obj in array)
{
[result appendString:[obj description]];
[result appendString:@", "];
}
// 获取字符串长度
NSUInteger len = [result length];
// 去掉字符串最后的两个字符
[result deleteCharactersInRange:NSMakeRange(len - 2, 2)];
[result appendString:@"]"];
return result;
}
int main(int argc , char * argv[])
{
@autoreleasepool{
NSSet* set = [NSSet setWithObjects:
[[FKUser alloc] initWithName:@"sun" pass:@"123"],
[[FKUser alloc] initWithName:@"bai" pass:@"345"],
[[FKUser alloc] initWithName:@"sun" pass:@"123"],
[[FKUser alloc] initWithName:@"tang" pass:@"178"],
[[FKUser alloc] initWithName:@"niu" pass:@"155"],
nil];
NSLog(@"set集合元素的个数:%ld" , [set count]);
NSLog(@"%@" , NSCollectionToString(set));
}
}


运行结果:

2015-10-03 20:28:38.599 923[2877:165034] ===hash===
2015-10-03 20:28:38.600 923[2877:165034] ===hash===
2015-10-03 20:28:38.601 923[2877:165034] ===hash===
2015-10-03 20:28:38.601 923[2877:165034] ===hash===
2015-10-03 20:28:38.602 923[2877:165034] ===hash===
2015-10-03 20:28:38.602 923[2877:165034] set集合元素的个数:4
2015-10-03 20:28:38.603 923[2877:165034] [<FKUser[name=bai, pass=345]>, <FKUser[name=sun, pass=123]>, <FKUser[name=tang, pass=178]>, <FKUser[name=niu, pass=155]>]


说明:

在 上面的代码中,如果没有重写Hash 方法的话,会输出:

2015-10-03 20:37:02.556 923[2973:170275] set集合元素的个数:5
2015-10-03 20:37:02.558 923[2973:170275] [<FKUser[name=sun, pass=123]>, <FKUser[name=sun, pass=123]>, <FKUser[name=niu, pass=155]>, <FKUser[name=bai, pass=345]>, <FKUser[name=tang, pass=178]>]


上面的代码输出 NSSet 集合中只包含4个元素,,但输出来了
===hash===
5次,说明 NSSet 集合每次添加一个集合 元素,总会调用该元素的 Hash 方法——所有的对象都有 hash 方法,——因为所有的对象都继承了 NSObject 类。

特别注意:

如果需要把一个对象放入 NSSet 集合中,如果重写该对象对应类的
isEqual:
方法,也应该重写其 Hash 方法。——其规则是:如果2个 对象通过
isEqual:
方法比较返回YES时,这2个对象的 Hash方法的返回值也应该相同。



Hash 方法

重写Hash 方法的基本原则:

程序 运行过程中,同一个 对象多次调用 Hash 应该返回想通过的值。

如果2个 对象通过
isEqual:
方法比较返回YES时,这2个对象的 Hash方法的返回值也应该相同。

对象中作为
isEqual:
比较标准的实例变量,都应该用来计算hashCode值。

重写Hash 方法的步骤:



NSMutableSet 的功能与用法

由于NSMutableSet可以动态添加集合元素,因此,创建 NSSet 集合时可指定底层 Hash表的初始容量。

类似于 NSMutableArray 与 NSArray的关系,NSMutableSet主要在 NSSet的基础上,增加了添加元素,删除元素的方法,并增加了对集合计算交集,并集,差集的方法。

1.
addObject:
向集合中添加单个元素

2.
removeObject:
从集合中删除单个元素

3.
removeAllObjects:
从集合中删除所有元素

4.
addObjectsFromArray:
使用数组作为参数,想 NSSet 集合中添加参数数组中的所有元素。

5.
unionSet:
计算2个集合的并集。

6.
minusSet:
计算2个集合的差集。

7.
intersectSet:
计算2个集合的交集。

8.
setSet:
用后一个集合的元素替换已有集合中所有的元素。

示例程序:

NSMutableSetTest.m

//  Created by yeeku on 2013-4-22.
//  Copyright (c) 2013年 crazyit.org. All rights reserved.

#import <Foundation/Foundation.h>

// 定义一个函数,该函数可把NSArray或NSSet集合转换为字符串
NSString* NSCollectionToString(id collection)
{
NSMutableString* result = [NSMutableString
stringWithString:@"["];
// 使用快速枚举遍历NSSet集合
for(id obj in collection)
{
[result appendString:[obj description]];
[result appendString:@", "];
}
// 获取字符串长度
NSUInteger len = [result length];
// 去掉字符串最后的两个字符
[result deleteCharactersInRange:NSMakeRange(len - 2, 2)];
[result appendString:@"]"];
return result;
}
int main(int argc , char * argv[])
{
@autoreleasepool{
// 创建一个初始容量为10的Set集合
NSMutableSet* set = [NSMutableSet setWithCapacity:10];
[set addObject:@"疯狂iOS讲义"];
NSLog(@"添加1个元素后:%@" , NSCollectionToString(set));
[set addObjectsFromArray: [NSArray
arrayWithObjects:@"疯狂Android讲义"
, @"疯狂Ajax讲义" , @"疯狂XML讲义" ,nil]];
NSLog(@"使用NSArray添加3个元素后:%@" , NSCollectionToString(set));
[set removeObject:@"疯狂XML讲义"];
NSLog(@"删除1个元素后:%@" , NSCollectionToString(set));
// 再次创建一个Set集合
NSSet* set2 = [NSSet setWithObjects:
@"孙悟空", @"疯狂iOS讲义" , nil];
// 计算两个集合的并集,直接改变set集合的元素
[set unionSet: set2];
// 计算两个集合的差集,直接改变set集合的元素
//      [set minusSet: set2];
// 计算两个集合的交集,直接改变set集合的元素
//      [set intersectSet: set2];
// 用set2的集合元素替换set的集合元素,直接改变set集合的元素
//      [set setSet: set2];
NSLog(@"%@" , NSCollectionToString(set));
}
}


编译运行结果:

2015-10-04 08:46:05.754 923[1130:18827] 添加1个元素后:[疯狂iOS讲义]
2015-10-04 08:46:05.756 923[1130:18827] 使用NSArray添加3个元素后:[疯狂iOS讲义, 疯狂Ajax讲义, 疯狂Android讲义, 疯狂XML讲义]
2015-10-04 08:46:05.757 923[1130:18827] 删除1个元素后:[疯狂iOS讲义, 疯狂Ajax讲义, 疯狂Android讲义]
2015-10-04 08:46:05.759 923[1130:18827] [疯狂iOS讲义, 疯狂Ajax讲义, 疯狂Android讲义, 孙悟空]


NSCountedSet 的功能与用法

NSCountedSet是NSMutableSet的子类。NSCountedSet与普通NSMutableSet的不同的是:它为每个元素额外维护一个添加次数的状态。

当向NSCountedSet中添加元素时,如果没有这个元素,就添加进去,同时该元素的添加次数标注为1,再添加同一元素时,不会接纳该元素,但会使添加次数+1.当从NSCountedSet中删除元素时,只是将该元素的添加次数-1,只有该元素的添加次数变为0时,该元素才会真正从NSCountedSet集合中删除。

NSCountedSet提供的返回某个元素的添加次数的方法是:

countForObject:
——获取指定元素的添加次数

示例代码:

NSCountedSetTest.m

//  Created by yeeku on 2013-4-22.
//  Copyright (c) 2013年 crazyit.org. All rights reserved.

#import <Foundation/Foundation.h>

// 定义一个函数,该函数可把NSArray或NSSet集合转换为字符串
NSString* NSCollectionToString(id collection)
{
NSMutableString* result = [NSMutableString
stringWithString:@"["];
// 使用快速枚举遍历NSSet集合
for(id obj in collection)
{
[result appendString:[obj description]];
[result appendString:@", "];
}
// 获取字符串长度
NSUInteger len = [result length];
// 去掉字符串最后的两个字符
[result deleteCharactersInRange:NSMakeRange(len - 2, 2)];
[result appendString:@"]"];
return result;
}
int main(int argc , char * argv[])
{
@autoreleasepool{
NSCountedSet* set = [NSCountedSet setWithObjects:
@"疯狂iOS讲义" , @"疯狂Android讲义",
@"疯狂Ajax讲义" , nil];
[set addObject:@"疯狂iOS讲义"];
[set addObject:@"疯狂iOS讲义"];
// 输出集合元素
NSLog(@"%@" , NSCollectionToString(set));
// 获取指定元素的添加顺序
NSLog(@"\"疯狂iOS讲义\"的添加次数为:%ld"
, [set countForObject:@"疯狂iOS讲义"]);
// 删除元素
[set removeObject:@"疯狂iOS讲义"];
NSLog(@"删除\"疯狂iOS讲义\"1次后的结果:%@"
,  NSCollectionToString(set));
NSLog(@"删除\"疯狂iOS讲义\"1次后的添加次数为:%ld"
, [set countForObject:@"疯狂iOS讲义"]);
// 重复删除元素
[set removeObject:@"疯狂iOS讲义"];
[set removeObject:@"疯狂iOS讲义"];
NSLog(@"删除\"疯狂iOS讲义\"3次后的结果:%@"
, NSCollectionToString(set));
}
}


编译运行结果:

2015-10-04 08:59:16.780 923[1168:23315] [疯狂iOS讲义, 疯狂Android讲义, 疯狂Ajax讲义]
2015-10-04 08:59:16.782 923[1168:23315] "疯狂iOS讲义"的添加次数为:3
2015-10-04 08:59:16.782 923[1168:23315] 删除"疯狂iOS讲义"1次后的结果:[疯狂iOS讲义, 疯狂Android讲义, 疯狂Ajax讲义]
2015-10-04 08:59:16.783 923[1168:23315] 删除"疯狂iOS讲义"1次后的添加次数为:2
2015-10-04 08:59:16.783 923[1168:23315] 删除"疯狂iOS讲义"3次后的结果:[疯狂Android讲义, 疯狂Ajax讲义]


有序集合(NSOrderedSet 与 NSMutableOrderedSet)

先说明一下,NSOrderedSet 与 NSMutableOrderedSet(后者是前者的子类),这俩货是个奇葩的综合体。既具有 NSSet 集合的特征,也具有数组NSArray类似的 功能。

NSMutableOrderedSet 是NSOrderedSet的拓展,增加了增,删,替换,集合排序,交集,并集,差集等功能。

NSOrderedSet 不允许元素重复——这与NSSet相同。可以保持元素的添加顺序,而且每个元素都有索引,可以根据索引操作元素。——这又与 NSArray 相似。

示例代码:

#import <Foundation/Foundation.h>

// 定义一个函数,该函数可把NSArray或NSSet集合转换为字符串
NSString* NSCollectionToString(id collection)
{
NSMutableString* result = [NSMutableString
stringWithString:@"["];
// 使用快速枚举遍历NSSet集合
for(id obj in collection)
{
[result appendString:[obj description]];
[result appendString:@", "];
}
// 获取字符串长度
NSUInteger len = [result length];
// 去掉字符串最后的两个字符
[result deleteCharactersInRange:NSMakeRange(len - 2, 2)];
[result appendString:@"]"];
return result;
}
int main(int argc , char * argv[])
{
@autoreleasepool{
// 创建NSOrderedSet集合,故意使用重复的元素
// 程序看到程序只会保留一个元素
NSOrderedSet* set = [NSOrderedSet orderedSetWithObjects:
[NSNumber numberWithInt:40],
[NSNumber numberWithInt:12],
[NSNumber numberWithInt:-9],
[NSNumber numberWithInt:28],
[NSNumber numberWithInt:12],
[NSNumber numberWithInt:17],
nil];
NSLog(@"%@" , NSCollectionToString(set));
// 下面方法都是根据索引来操作集合元素
// 获取第一个元素
NSLog(@"set集合的第一个元素 :%@" , [set firstObject]);
// 获取最后一个元素
NSLog(@"set集合的最后一个元素 :%@" , [set lastObject]);
// 获取指定索引处的元素
NSLog(@"set集合中索引为2的元素 :%@" , [set objectAtIndex:2]);
NSLog(@"28在set集合中的索引为:%ld" , [set indexOfObject:[NSNumber numberWithInt:28]]);
// 对集合进行过滤,获取元素值大于20的集合元素的索引
NSIndexSet* indexSet = [set indexesOfObjectsPassingTest:
^(id obj, NSUInteger idx, BOOL *stop)
{
return (BOOL)([obj intValue] > 20);
}];
NSLog(@"set集合中元素值大于20的元素的索引为:%@" , indexSet);
}
}


编译运行结果:

2015-10-04 09:10:50.426 923[1213:27659] [40, 12, -9, 28, 17]
2015-10-04 09:10:50.427 923[1213:27659] set集合的第一个元素 :40
2015-10-04 09:10:50.427 923[1213:27659] set集合的第一个元素 :17
2015-10-04 09:10:50.428 923[1213:27659] set集合中索引为2的元素 :-9
2015-10-04 09:10:50.428 923[1213:27659] 28在set集合中的索引为:3
2015-10-04 09:10:50.429 923[1213:27659] set集合中元素值大于20的元素的索引为:<NSIndexSet: 0x7fdb3840c310>[number of indexes: 2 (in 2 ranges), indexes: (0 3)]
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息