NSSet集合类似一个罐子,把对象“丢进”NSSet集合,集合里多个对象之间没有明显的顺序。NSSet不允许包含相同的元素, 如果试图把两个相同的元素放在同一个NSSet集合中,则只会保留一个元素。
NSSet的功能与用法
NSSet按Hash算法来存储集合中的元素,因此具有很好的存取和查找性能。
NSSet不能保证元素的添加顺序,顺序有可能发生变化。与NSArray相比,NSSet最大的区别是元素没有索引,因此不能根据索引来操作元素。
实际上,NSArray与NSSet依然有大量的相似之处,NSSet与NSArray在如下方面的调用机制都非常相似:
- 都可以通过count方法获取集合元素的数量
- 都可通过快速枚举来遍历集合元素。
- 都可通过objectEnumerator方法获取NSEnumerator枚举器对集合元素进行遍历。
- 都提供了makeObjectPerformSelector、makeObjectsPerformSelector:withObject:方法对集合元素整体调用某个方法,以及enumerateObjectsUsingBlock:、enumerateObjectsWithOptions:usingBlock:对集合整体或部分元素迭代执行代码块。
- 都提供了valueForKey:和setValue:forKey:方法对集合元素整体进行KVC编程。
- 都提供了集合的所有元素和部分元素进行KVO编程的方法。
NSSet代表集合元素无索引且不允许重复的集合;而NSArray则代表集合元素有索引且允许重复的集合。因此,可认为NSSet代表通用的集合,而NSArray则在NSSet基础上扩展了功能:主要是让集合有索引,因此,NSArray允许通过索引来操作集合元素。
NSSet同样提供了类方法和实例方法来创建NSSet,只是类方法以set开头,而实例方法则以init开头。
下面示例示范了NSSet集合的基本用法
#import <Foundation/Foundation.h>
//定义一个函数,该函数可把NSAarray或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, const char * argv[]) {
@autoreleasepool {
//用四个元素初始化NSSet集合
//故意传入两个相等的元素,NSSet集合只会保留一个元素
NSSet* set1 = [NSSet setWithObjects:@"疯狂iOS讲义" , @"疯狂Android讲义",
@"疯狂Ajax讲义" , @"疯狂iOS讲义" , nil];
//程序输出set1集合中元素个数为3
NSLog(@"set1集合中元素个数为%ld" , [set1 count]);
NSLog(@"set1集合为:%@" , NSCollectionToString(set1));
NSSet* set2 = [NSSet setWithObjects:@"孙悟空", @"疯狂Android讲义" , @"猪八戒" , nil];
NSLog(@"set2集合:%@" , NSCollectionToString(set2));
//向set1集合中添加单个元素,将添加元素后生成的新集合赋给set1
set1 = [set1 setByAddingObject:@"疯狂Swift讲义"];
NSLog(@"添加一个元素后:%@" , NSCollectionToString(set1));
//使用NSSet集合向set1集合中添加多个元素,相当于计算两个集合的并集
NSSet* s = [set1 setByAddingObjectsFromSet:set2];
NSLog(@"set1和set2的并集:%@" , NSCollectionToString(s));
BOOL b = [set1 intersectsSet:set2];
NSLog(@"set1和set2是否有交集:%d" , b);
BOOL bo = [set2 isSubsetOfSet:set1];
NSLog(@"set2是否为set1的子集:%d" , bo);
//判断NSSet集合是否包含指定元素
BOOL bb = [set1 containsObject:@"疯狂Ajax讲义"];
NSLog(@"set1是否包含\"疯狂iOS讲义\":%d" , bb);
//下面两行代码将取出相同的元素,但取出哪个元素是不确定的
NSLog(@"set1取出一个元素:%@" , [set1 anyObject]);
NSLog(@"set2取出一个元素:%@" , [set1 anyObject]);
//使用代码块对集合元素进行过滤
NSSet* filteredSet = [set1 objectsPassingTest:^(id obj , BOOL *stop)
{
return (BOOL)([obj length] > 8);
}];
NSLog(@"set1中的元素的长度大于8的集合元素有:%@" , NSCollectionToString(filteredSet));
}
return 0;
}
效果:
NSSet判断集合元素重复的标准
//FKUser.h
#import <Foundation/Foundation.h>
@interface FKUser : NSObject
@property (nonatomic , copy) NSString* name;
- (id) initWithName: (NSString*)aName;
- (void) say:(NSString*) content;
@end
//FKUser.m
#import "FKUser.h"
@implementation FKUser
- (id) initWithName:(NSString *) aName {
if (self = [super init]) {
self.name = aName;
}
return self;
}
- (BOOL) isEqual:(id)other
{
if (self == other) {
return YES;
}
if ([other class] == FKUser.class) {
FKUser* target = (FKUser*)other;
return [self.name isEqualToString:target.name];
}
return NO;
}
- (id) description {
return self.name;
}
- (void) say:(NSString *)content {
NSLog(@"%@说:%@" , self.name , content);
}
@end
//main.m
#import <Foundation/Foundation.h>
#import "FKUser.h"
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, const char * argv[]) {
@autoreleasepool {
NSSet* set = [NSSet setWithObjects:
[[FKUser alloc] initWithName:@"sun"],
[[FKUser alloc] initWithName:@"bai"],
[[FKUser alloc] initWithName:@"sun"],
[[FKUser alloc] initWithName:@"niu"],nil];
NSLog(@"set集合元素的z个数为:%ld" , [set count]);
NSLog(@"%@" , NSCollectionToString(set));
//由于没重写hash方法,所有即使name的值相同,由于hashcode不同,系统还是会把它们当作两个元素
/*
一、重写hash方法的基本规则:
1、在程序运行过程中,同一个对象多次调用hash方法应该返回相同的值
2、当两个对象通过isEqual:方法比较返回YES时,这两个对象的hash方法应返回相等的值
3、对象中作为isEqual:方法比较标准的成员变量,都应该用来计算hashCode值
二、重写hash的一般步骤:
1、把对象内每个有意义的成员变量(即每个用作isEqual:方法比较标准的实例变量)计算出一个int类型的hashCode值。
2、用第一步计算出来的多个hashCode值组合计算出一个hashCode值返回。例如:如下代码
return [f1 hash] + [f2 hash];
注:
为了避免直接相加产生偶然相等,可以通过为各实例变量的hashCode值乘以任意一个质数g后再相加。例如:
return [f1 hash] * 31 + [f2 hash];
*/
}
return 0;
}
效果:
所以需要重写hash方法,为FKUse类重写 hash方法:
- (NSUInteger) hash
{
NSLog(@"===hash===");
NSUInteger nameHash = self.name == nil ? 0 : [self.name hash];
return nameHash;
}
效果:
NSMutableSet的功能与用法
NSMutableSet继承了NSSet,它代表一个集合元素可变的NSSet集合。由于NSMutableSet可以动态的添加集合元素,因此,创建NSMutableSet集合时可指定底层Hash表的 初始容量。
类似于NSMutableArray与NSArray的关系,NSMutableSet主要在NSSet基础上增加了添加元素、删除元素的方法,并增加了对集合计算交集、并集、差集的方法。
示例:
#import <Foundation/Foundation.h>
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, const char * argv[]) {
@autoreleasepool {
NSMutableSet* set = [NSMutableSet setWithCapacity:10];
[set addObject:@"疯狂iOS讲义"];
NSLog(@"添加一个元素后,%@" , NSCollectionToString(set));
[set addObjectsFromArray:[NSArray arrayWithObjects:@"疯狂Android讲义",
@"疯狂Ajax讲义",
@"疯狂XML讲义",
nil]];
NSLog(@"使用NSArray添加3个元素后:%@" , NSCollectionToString(set));
[set removeObject:@"疯狂iOS讲义"];
NSLog(@"删除一个元素后:%@" , NSCollectionToString(set));
//再次创建一个NSSet集合
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));
}
return 0;
}
效果:
NSCountedSet的功能与用法
NSCountedSet是NSMutableSet的子类,与普通NSMutableSet集合不同的是,NSCountedSet为每个元素额外维护一个添加次数的状态。当程序向NSCountedSet中添加一个元素时,如果NSCountedSet集合中不包含该元素,那么NSCountedSet真正接纳该元素,并将该元素的添加次数标注为1:当程序向NSCountedSet中添加一个元素时,如果NSCountedSet集合中已经包含该元素,那么NSCountedSet不会接纳该元素,但会将该元素的添加次数加一。 当程序从NSCountedSet集合中删除元素时,NSCountedSet只是将该元素的添加次数减一,只有当该元素的添加次数变为0时,该元素才会真正从NSCountedSet集合中删除。
NSCountedSet提供了如下方法来返回某个元素的添加次数:
countedForObject:获取指定元素的添加次数。
#import <Foundation/Foundation.h>
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, const 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));
}
return 0;
}
效果: