OC学习13——Foundation框架中的集合
OC集合类是一些非常有用的工具类,它可以用于存储多个数量不等的对象,并可以实现常用的数据结构(栈、队列等),此外,OC集合还可用于保存具有映射关系的关联数组。OC的集合大致可以分为:NSArray、NSSet、NSDictionary三种体系。NSArray代表有序可重复的集合;NSSet代表无序不可重复的集合;NSDictionary代表具有映射关系的集合。显然,这三类集合分别对应这Java中的List、Set和Map。
集合类和数组不一样,数组元素既可以是基本类型的值,也可以是对象(实际上保存的是对象的指针变量),而集合里则只能保存对象(实际上保存的是对象的指针变量,但通常认为集合里爆粗你的是对象)。
一、数组(NSArray与NSMutableArray)
1、NSArray代表元素有序、可重复的一个集合,集合中每个元素都有其对应的顺序索引。NSArray集合允许使用重复元素,可以通过索引来访问指定位置的集合元素。NSArray分别提供了类方法和实例方法来创建NSArray,两种创建方式基本类似,只是类方法以array开头,实例方法以init开头。集中常见的NSArray创建对象的方法如下:
- array:创建一个不包含任何元素的空NSArray
- arrayWithContentsOfFile:/ initWithContentsOfFile::读取文件内容来创建NSArray
- arrayWithObject:/ initWithObject::创建只包含指定元素的NSArray
- arrayWithObjects:/ initWithObjects::创建包含指定的N个元素的NSArray
- 还可以用@[元素1,元素2,元素3,。。。]来创建NSArray
参考NSArray类的文档,可以看到NSArray集合的方法大致包含如下几类:
- 查询集合元素在NSArray中的索引,即下标
- 根据索引值取出NSArray集合中的元素
- 对集合元素整体调用方法
- 对NSArray集合进行排序
- 取出NSArray集合中的部分集合组成新集合
1 #import <Foundation/Foundation.h> 2 3 int main(int argc , char * argv[]) 4 { 5 @autoreleasepool{ 6 //创建一个NSArray对象,直接传入多个元素,其中最后一个nil表示NSArray元素结束,其实这个nil并不会存入NSArray中 7 NSArray* array = [NSArray arrayWithObjects: 8 @"疯狂iOS讲义", @"疯狂Android讲义" 9 , @"疯狂Ajax讲义", @"疯狂XML讲义" 10 , @"Struts 2.x权威指南" , nil]; 11 NSLog(@"第一个元素:%@" , [array objectAtIndex:0]); 12 NSLog(@"索引为1的元素:%@" , [array objectAtIndex:1]); 13 NSLog(@"最后一个元素:%@" , [array lastObject]); 14 // 获取索引从2~5的元素组成的新集合,NSIndexSet类与NSSet基本类似,区别只是NSIndexSet 集合主要用于保存所引致,因此,其集合元素都是NSUInteger对象 15 NSArray* arr1 = [array objectsAtIndexes: [NSIndexSet 16 indexSetWithIndexesInRange:NSMakeRange(2, 3)]]; 17 NSLog(@"%@" , arr1); 18 // 获取元素在集合中的位置 19 NSLog(@"疯狂Android讲义的位置为:%ld" , 20 [array indexOfObject:@"疯狂Android讲义"]); 21 // 获取元素在集合的指定范围中的位置 22 NSLog(@"在2~5范围疯狂Android讲义的位置为:%ld" , 23 [array indexOfObject:@"疯狂Android讲义" 24 inRange:NSMakeRange(2, 3)]); // ① 25 // 向数组的最后追加一个元素。 26 // 原NSArray本身并没有改变,只是将新返回的NSArray赋给array 27 array = [array arrayByAddingObject:@"孙悟空"]; 28 // 向array数组的最后追加另一个数组的所有元素。 29 // 原NSArray本身并没有改变,只是将新返回的NSArray赋给array 30 array = [array arrayByAddingObjectsFromArray: 31 [NSArray arrayWithObjects:@"宝玉" , @"黛玉" , nil]]; 32 for (int i = 0 ; i < array.count; i++) 33 { 34 // NSLog(@"%@" , [array objectAtIndex:i]); 35 // 上面代码也可简写为如下代码 36 NSLog(@"%@" , array[i]); 37 38 } 39 // 获取array数组中索引为5~8处的所有元素 40 NSArray* arr2 = [array subarrayWithRange: NSMakeRange(5, 3)]; 41 // 将NSArray集合的元素写入文件 42 [arr2 writeToFile:@"myFile.txt" atomically:YES]; 43 } 44 }
- 使用arrayByAddingObject:方法追加单个元素
- 使用arrayWithObjects:方法将另一个数组中所有元素追加到原数组的后面
- NSArray中判断集合中是否包含指定元素的方法就是依次用集合中的每一个元素与指定元素进行比较,而在集合中比较两个对象是否一样则是根据isEqual:方法来进行判断的
2、NSArray允许对集合中所有的元素或部分元素整体调用方法,如果只是简单地调用集合元素的方法,则可通过NSArray的如下两种方式来实现:
- makeObjectsPerformSelector:依次调用NSArray集合中每个元素的指定方法,该方法需传入一个SEL参数,用于指定调用那个方法
- makeObjectsPerformSelector:withObject::依次调用NSArray集合中每个元素的指定方法,该方法第一个参数需传入一个SEL参数,用于指定调用那个方法,第二个参数用于调用集合元素的方法时传入参数
如果希望对集合中所有元素进行隐式遍历,并使用集合元素来执行某一段代码,则可通过一下的方法来完成:
- enumerateObjectsUsingBlock::遍历集合中的所有元素,并依次使用元素来执行指定代码块
- enumerateObjectsWithOptions:usingBlock::遍历集合中的所有元素,并依次使用元素来执行指定代码块。该方法可额外传入一个参数,用于控制遍历的选项,如反向遍历。
- enumerateObjectsAtIndexes:options:usingBlock::遍历集合汇总指定范围内的元素,并依次使用元素来执行指定的代码块。
1 import <Foundation/Foundation.h> 2 #import "FKUser.h" 3 4 int main(int argc , char * argv[]) 5 { 6 @autoreleasepool{ 7 // 初始化NSArray对象 8 NSArray* array = [NSArray arrayWithObjects: 9 [[FKUser alloc] initWithName:@"sun" pass:@"123"], 10 [[FKUser alloc] initWithName:@"bai" pass:@"345"], 11 [[FKUser alloc] initWithName:@"zhu" pass:@"654"], 12 [[FKUser alloc] initWithName:@"tang" pass:@"178"], 13 [[FKUser alloc] initWithName:@"niu" pass:@"155"], 14 nil]; 15 // 对集合元素整体调用方法 16 [array makeObjectsPerformSelector:@selector(say:) 17 withObject:@"下午好,NSArray真强大!"]; 18 NSString* content = @"疯狂iOS讲义"; 19 // 迭代集合内指定范围内元素,并使用该元素来执行代码块 20 [array enumerateObjectsAtIndexes: 21 [NSIndexSet indexSetWithIndexesInRange:NSMakeRange(2,2)] 22 options:NSEnumerationReverse 23 // 代码块的第一个参数代表正在遍历的集合元素 24 // 代码块的第二个参数代表正在遍历的集合元素的索引 25 usingBlock: ^(id obj, NSUInteger idx, BOOL *stop) 26 { 27 NSLog(@"正在处理第%ld个元素:%@" , idx , obj); 28 [obj say:content]; 29 }]; 30 } 31 }
对NSArray进行排序:
- sortedArrayUsingFunction::该方法使用排序函数对集合元素进行排序,该排序函数必须返回NSOrderedDescending、NSOrderedAscending、NSOrderedSame这些枚举值,用以代表集合元素的大小。该方法返回一个排号需的新NSArray对象。
- sortedArrayUsingSelector::该方法使用集合元素自身的排序方法(compare:)对集合元素进行排序,该排序函数必须返回NSOrderedDescending、NSOrderedAscending、NSOrderedSame这些枚举值,用以代表集合元素的大小。该方法返回一个排号需的新NSArray对象。
- sortedArrayUsingComparator::该方法使用代码块对集合元素进行排序,该代码块必须返回NSOrderedDescending、NSOrderedAscending、NSOrderedSame这些枚举值,用以代表集合元素的大小。该方法返回一个排号需的新NSArray对象。
1 #import <Foundation/Foundation.h> 2 3 // 定义比较函数,根据两个对象的intValue进行比较 4 NSInteger intSort(id num1, id num2, void *context) 5 { 6 int v1 = [num1 intValue]; 7 int v2 = [num2 intValue]; 8 if (v1 < v2) 9 return NSOrderedAscending; 10 else if (v1 > v2) 11 return NSOrderedDescending; 12 else 13 return NSOrderedSame; 14 } 15 int main(int argc , char * argv[]) 16 { 17 @autoreleasepool{ 18 // 初始化一个元素为NSString的NSArray对象 19 NSArray* array1 = [NSArray arrayWithObjects: 20 @"Objective-C" , @"C" , @"C++" 21 , @"Ruby" , @"Perl" , @"Python" , nil]; 22 // 使用集合元素的compare:方法执行排序 23 array1 = [array1 sortedArrayUsingSelector: 24 @selector(compare:)]; 25 NSLog(@"%@" , array1); 26 // 初始化一个元素为NSNumber的NSArray对象 27 NSArray* array2 = [NSArray arrayWithObjects: 28 [NSNumber numberWithInt:20], 29 [NSNumber numberWithInt:12], 30 [NSNumber numberWithInt:-8], 31 [NSNumber numberWithInt:50], 32 [NSNumber numberWithInt:19], nil]; 33 // 使用intSort函数执行排序 34 array2 = [array2 sortedArrayUsingFunction:intSort 35 context:nil]; 36 NSLog(@"%@" , array2); 37 // 使用代码块对集合元素进行排序 38 NSArray* array3 = [array2 sortedArrayUsingComparator: 39 ^(id obj1, id obj2) 40 { 41 // 该代码块就是根据集合元素的 intValue进行比较 42 if ([obj1 intValue] > [obj2 intValue]) 43 { 44 return NSOrderedDescending; 45 } 46 if ([obj1 intValue] < [obj2 intValue]) 47 { 48 return NSOrderedAscending; 49 } 50 return NSOrderedSame; 51 }]; 52 NSLog(@"%@" , array3); 53 } 54 }
对NSArray进行遍历:
- objectEnumerator:返回NSArray集合的顺序枚举器
- reverseObjectEnumerator:返回NSArray集合的逆序顺序枚举器
- 快速枚举(for...in)
上面两个方法都返回一个NSEnumerator枚举器,该枚举器只包含两个方法:
- allObjects:获取被枚举集合中的所遇元素
- nextObject:获取被枚举集合中的下一个元素
1 #import <Foundation/Foundation.h> 2 3 int main(int argc , char * argv[]) 4 { 5 @autoreleasepool{ 6 // 读取前面写入磁盘的文件,用文件内容来初始化NSArray集合 7 NSArray* array = [NSArray arrayWithContentsOfFile: 8 @"myFile.txt"]; 9 // 获取NSArray的顺序枚举器 10 NSEnumerator* en = [array objectEnumerator]; 11 id object; 12 while(object = [en nextObject]) 13 { 14 NSLog(@"%@" , object); 15 } 16 NSLog(@"------下面逆序遍历------"); 17 // 获取NSArray的逆序枚举器 18 en = [array reverseObjectEnumerator]; 19 while(object = [en nextObject]) 20 { 21 NSLog(@"%@" , object); 22 } 23 } 24 }
1 #import <Foundation/Foundation.h> 2 3 int main(int argc , char * argv[]) 4 { 5 @autoreleasepool{ 6 // 读取前面写入磁盘的文件,用文件内容来初始化NSArray集合 7 NSArray* array = [NSArray arrayWithContentsOfFile: 8 @"myFile.txt"]; 9 for(id object in array) 10 { 11 NSLog(@"%@" , object); 12 } 13 } 14 }
3、NSArray代表集合不可变元素,一旦NSArray创建成功,程序就不能向集合中添加新的元素,不能删除集合中已有的元素,也不能替换集合元素。NSArray有一个子类NSMutableArray,NSMutableArray代表集合元素可变的集合,因此,程序可以向集合中添加新元素,可以删除集合中已有的元素。
由于NSMutableArray是NSArray的子类,所以继承了NSArray中的所有方法,此外还提供了一系列可以对数组进行修改的方法:
- 添加集合元素的方法:这类方法以add开头
- 删除集合元素的方法:这类方法以remove开头
- 替换集合元素的方法:这类方法以replace开头
- 对集合本身排序的方法:这类方法以sort开头
1 #import <Foundation/Foundation.h> 2 3 // 定义一个函数,该函数用于把NSArray集合转换为字符串 4 NSString* NSCollectionToString(NSArray* array) 5 { 6 NSMutableString* result = [NSMutableString 7 stringWithString:@"["]; 8 for(id obj in array) 9 { 10 [result appendString:[obj description]]; 11 [result appendString:@", "]; 12 } 13 // 获取字符串长度 14 NSUInteger len = [result length]; 15 // 去掉字符串最后的两个字符 16 [result deleteCharactersInRange:NSMakeRange(len - 2, 2)]; 17 [result appendString:@"]"]; 18 return result; 19 } 20 int main(int argc , char * argv[]) 21 { 22 @autoreleasepool{ 23 // 读取前面写入磁盘的文件,用文件内容来初始化NSMutableArray集合 24 NSMutableArray* array = [NSMutableArray 25 arrayWithContentsOfFile:@"myFile.txt"]; 26 // 向集合最后添加一个元素 27 [array addObject:@"疯狂iOS讲义"]; 28 NSLog(@"最后追加一个元素后:%@" , NSCollectionToString(array)); 29 // 使用NSArray向集合尾部添加多个元素 30 [array addObjectsFromArray: [NSArray 31 arrayWithObjects:@"张飞" , @"关羽",nil]]; 32 NSLog(@"最后追加两个元素后:%@" , NSCollectionToString(array)); 33 // 向集合的指定位置插入一个元素 34 [array insertObject:@"疯狂Ajax讲义" atIndex:2]; 35 NSLog(@"在索引为2处插入一个元素后:%@" 36 , NSCollectionToString(array)); 37 // 使用NSArray向集合指定位置插入多个元素 38 [array insertObjects: [NSArray 39 arrayWithObjects:@"武松" , @"林冲",nil] 40 atIndexes:[NSIndexSet indexSetWithIndexesInRange 41 :NSMakeRange(3,2)]]; 42 NSLog(@"插入多个元素后:%@" , NSCollectionToString(array)); 43 // 删除集合最后一个元素 44 [array removeLastObject]; 45 NSLog(@"删除最后一个元素后:%@" , NSCollectionToString(array)); 46 // 删除集合中指定索引处的元素 47 [array removeObjectAtIndex:5]; 48 NSLog(@"删除索引为5处的元素后:%@" , NSCollectionToString(array)); 49 // 删除2~5处元素 50 [array removeObjectsInRange:NSMakeRange(2, 3)]; 51 NSLog(@"删除索引为2~5处的元素后:%@" 52 , NSCollectionToString(array)); 53 // 替换索引为2处的元素 54 [array replaceObjectAtIndex:2 withObject:@"疯狂Android讲义"]; 55 NSLog(@"替换索引为2处的元素后:%@" 56 , NSCollectionToString(array)); 57 } 58 }
4、NSArray是一个容纳多个对象的集合,NSArray允许直接对集合中的所有元素进行整体的KVC编码,NSArray提供了如下两个方法:
- setValue:forKey::将NSArray集合中所有元素的指定key对应属性或实例变量设置为value
- valueForKey::返回该NSArray集合中所有元素的指定key组成的NSArray对象
此外,NSArray还为集合中所有元素或部分元素进行KVO编程提供了如下方法,但是根据Apple官方文档,NSArray本身不能被监听,所以调用此方法会引发异常。
- addObserver:toObjectsAtIndexs:forKeyPath:options:context::为集合中指定索引处的元素添加KVO监听器。
- removeObserver:fromObjectsAtIndexs:forKeyPath::为集合中指定索引处的元素删除KVO监听器。
1 #import <Foundation/Foundation.h> 2 #import "FKUser.h" 3 // 定义一个函数,该函数用于把NSArray集合转换为字符串 4 NSString* NSCollectionToString(NSArray* array) 5 { 6 NSMutableString* result = [NSMutableString 7 stringWithString:@"["]; 8 for(id obj in array) 9 { 10 [result appendString:[obj description]]; 11 [result appendString:@", "]; 12 } 13 // 获取字符串长度 14 NSUInteger len = [result length]; 15 // 去掉字符串最后的两个字符 16 [result deleteCharactersInRange:NSMakeRange(len - 2, 2)]; 17 [result appendString:@"]"]; 18 return result; 19 } 20 int main(int argc , char * argv[]) 21 { 22 @autoreleasepool{ 23 // 初始化NSArray对象 24 NSArray* array = [NSArray arrayWithObjects: 25 [[FKUser alloc] initWithName:@"sun" pass:@"123"], 26 [[FKUser alloc] initWithName:@"bai" pass:@"345"], 27 [[FKUser alloc] initWithName:@"zhu" pass:@"654"], 28 [[FKUser alloc] initWithName:@"tang" pass:@"178"], 29 [[FKUser alloc] initWithName:@"niu" pass:@"155"], 30 nil]; 31 // 获取所有集合元素的name属性组成的新集合 32 id newArr = [array valueForKey:@"name"]; 33 NSLog(@"%@" , NSCollectionToString(newArr)); 34 // 对集合中所有元素整体进行KVC编程 35 // 将所有集合元素的name属性改为"新名字" 36 [array setValue:@"新名字" forKey:@"name"]; 37 NSLog(@"%@" , NSCollectionToString(array)); 38 } 39 }
二、集合(NSSet和NSMutableSet)
NSSet和NSMutableSet的区别与NSArray和NSMutableArray之间的区别是一样的。NSSet表示元素个数和内容均不可变的集合,NSMutableSet则表示元素个数和内容可变的集合。
1、NSSet是一个广泛使用的集合,NSSet按Hash算法来储存集合中的元素,因此具有很好的存取和查找性能。也正因为是才用Hash算法储存元素,NSSet不能保证元素的添加顺序,顺序可能会发生变化。因此,与NSArray相比,NSSet的最大的区别就说元素没有索引,不能根据根据索引来操作元素。
NSSet与NSArray的相同点:
- 都可以通过count方法获取集合元素的数量
- 都可以通过快速枚举进行遍历
- 都可以通过objectEnumerator方法获取NSEnumerator枚举器对集合元素进行遍历。由于NSSet是无序的,所以提供反向枚举器也没有什么意义
- 都提供makeObjectsPerformSelector:、makeObjectsPerformSelector:withObject::对集合元素整体调用某个方法,以及enumerateObjectsUsingBlock:、enumerateObjectsWithOptions:usingBlock:、
enumerateObjectsAtIndexes:options:usingBlock对集合整体或部分元素迭代执行代码块
- 都提供了setValue:forKey:、valueForKey:方法对集合元素进行整体KVC编程
1 #import <Foundation/Foundation.h> 2 3 // 定义一个函数,该函数可把NSArray或NSSet集合转换为字符串 4 NSString* NSCollectionToString(id collection) 5 { 6 NSMutableString* result = [NSMutableString 7 stringWithString:@"["]; 8 // 使用快速枚举遍历NSSet集合 9 for(id obj in collection) 10 { 11 [result appendString:[obj description]]; 12 [result appendString:@", "]; 13 } 14 // 获取字符串长度 15 NSUInteger len = [result length]; 16 // 去掉字符串最后的两个字符 17 [result deleteCharactersInRange:NSMakeRange(len - 2, 2)]; 18 [result appendString:@"]"]; 19 return result; 20 } 21 int main(int argc , char * argv[]) 22 { 23 @autoreleasepool{ 24 // 用4个元素初始化NSSet集合, 25 // 故意传入两个相等的元素, NSSet集合只会保留一个元素 26 NSSet* set1 = [NSSet setWithObjects: 27 @"疯狂iOS讲义" , @"疯狂Android讲义", 28 @"疯狂Ajax讲义" ,@"疯狂iOS讲义" , nil]; 29 // 程序输出set1集合中元素个数为3 30 NSLog(@"set1集合中元素个数为%ld" , [set1 count]); 31 NSLog(@"s1集合:%@" , NSCollectionToString(set1)); 32 NSSet* set2 = [NSSet setWithObjects: 33 @"孙悟空" , @"疯狂Android讲义", 34 @"猪八戒" , nil]; 35 NSLog(@"s2集合:%@" , NSCollectionToString(set2)); 36 // 向set1集合中添加单个元素,将添加元素后生成的新集合赋给set1 37 set1 = [set1 setByAddingObject:@"Struts 2.1权威指南"]; 38 NSLog(@"添加一个元素后:%@" , NSCollectionToString(set1)); 39 // 使用NSSet集合向set1集合中添加多个元素,相当计算两个集合的并集 40 NSSet* s = [set1 setByAddingObjectsFromSet:set2]; 41 NSLog(@"set1与set2的并集:%@" , NSCollectionToString(s)); 42 // 计算两个NSSet集合的是否有交集 43 BOOL b = [set1 intersectsSet:set2]; 44 NSLog(@"set1与set2是否有交集:%d" , b);// 将输出代表YES的1 45 // 判断set2是否是set1的子集 46 BOOL bo = [set2 isSubsetOfSet:set1]; 47 NSLog(@"set2是否为set1的子集:%d" , bo);// 将输出代表NO的0 48 // 判断NSSet集合是否包含指定元素 49 BOOL bb = [set1 containsObject:@"疯狂Ajax讲义"]; 50 NSLog(@"set1是否包含\"疯狂Ajax讲义\":%d" , bb);// 将输出代表YES的1 51 // 下面两行代码将取出相同的元素,但取出哪个元素是不确定的。 52 NSLog(@"set1取出一个元素:%@" 53 , [set1 anyObject]); 54 NSLog(@"set1取出一个元素:%@" 55 , [set1 anyObject]); 56 // 使用代码块对集合元素进行过滤 57 NSSet* filteredSet = [set1 objectsPassingTest: 58 ^(id obj, BOOL *stop) 59 { 60 return (BOOL)([obj length] > 8); 61 }]; 62 NSLog(@"set1中元素的长度大于8的集合元素有:%@" 63 , NSCollectionToString(filteredSet)); 64 } 65 }
2、前面已经说过了,NSSet集合中的元素是不能重复的,因此每次再加入一个新元素时,需要判断新元素是否与集合中已有元素重复。判断标准是这样:
- 先调用hash方法得到该对象的hashCode值,然后根据该hashCode值决定该对象在底层hash表中的存储位置,如果该hashCode值对应的存储位置上没有存储元素,则系统将直接将该对象存储到对应的位置上
- 如果该hashCode值对应的位置已经有元素了,表明集合中已有元素的hashCode与该对象相同,接下来我们通过isEqual:方法判断两个元素是否相等,如果相等,则NSSet认为两个元素相等,该对象则添加失败。如果不想等,则NSSet依然认为这两个元素不一样,NSSet依然会将该对象添加到底层hash表对应hashCode值对应的位置,只是将这个位置形成一个链表。
1 #import <Foundation/Foundation.h> 2 #import "FKUser.h" 3 4 // 定义一个函数,该函数可把NSArray或NSSet集合转换为字符串 5 NSString* NSCollectionToString(id array) 6 { 7 NSMutableString* result = [NSMutableString 8 stringWithString:@"["]; 9 for(id obj in array) 10 { 11 [result appendString:[obj description]]; 12 [result appendString:@", "]; 13 } 14 // 获取字符串长度 15 NSUInteger len = [result length]; 16 // 去掉字符串最后的两个字符 17 [result deleteCharactersInRange:NSMakeRange(len - 2, 2)]; 18 [result appendString:@"]"]; 19 return result; 20 } 21 int main(int argc , char * argv[]) 22 { 23 @autoreleasepool{ 24 NSSet* set = [NSSet setWithObjects: 25 [[FKUser alloc] initWithName:@"sun" pass:@"123"], 26 [[FKUser alloc] initWithName:@"bai" pass:@"345"], 27 [[FKUser alloc] initWithName:@"sun" pass:@"123"], 28 [[FKUser alloc] initWithName:@"tang" pass:@"178"], 29 [[FKUser alloc] initWithName:@"niu" pass:@"155"], 30 nil]; 31 NSLog(@"set集合元素的个数:%ld" , [set count]); 32 NSLog(@"%@" , NSCollectionToString(set)); 33 } 34 }
所以如果需要某个类的对象保存到NSSet中,重写这个类的isEqual:方法和hash方法时,应该尽量保证两个对象通过isEqual:方法比较返回YES时,他们的hash方法返回值也是一样的。
3、NSMutableSet继承自NSSet,它代表一个集合元素可变的NSSet集合。由于NSMutableSet可以动态地添加集合元素,因此,创建NSMutableSet集合时可以指定底层hash表的初始容量。NSMutableSet在NSSet上增加了增加元素、删除元素的方法之外,还增加了对集合进行计算交集、并集和差集的方法。
- addObject::向集合中添加一个元素
- removeObject::向集合中删除有一个元素
- removeAllObjects::删除集合中所有元素
- addObjectsFromArray::向集合中添加NSArray中的所有元素
- unionSet::求两个NSSet的并集
- minusSet::求两个NSSet的差集
- intersectSet::求两个NSSet的交集
- setSet::用后一个集合的元素替换已有集合中所有的元素
1 #import <Foundation/Foundation.h> 2 3 4 // 定义一个函数,该函数可把NSArray或NSSet集合转换为字符串 5 NSString* NSCollectionToString(id collection) 6 { 7 NSMutableString* result = [NSMutableString 8 stringWithString:@"["]; 9 // 使用快速枚举遍历NSSet集合 10 for(id obj in collection) 11 { 12 [result appendString:[obj description]]; 13 [result appendString:@", "]; 14 } 15 // 获取字符串长度 16 NSUInteger len = [result length]; 17 // 去掉字符串最后的两个字符 18 [result deleteCharactersInRange:NSMakeRange(len - 2, 2)]; 19 [result appendString:@"]"]; 20 return result; 21 } 22 int main(int argc , char * argv[]) 23 { 24 @autoreleasepool{ 25 // 创建一个初始容量为10的Set集合 26 NSMutableSet* set = [NSMutableSet setWithCapacity:10]; 27 [set addObject:@"疯狂iOS讲义"]; 28 NSLog(@"添加1个元素后:%@" , NSCollectionToString(set)); 29 [set addObjectsFromArray: [NSArray 30 arrayWithObjects:@"疯狂Android讲义" 31 , @"疯狂Ajax讲义" , @"疯狂XML讲义" ,nil]]; 32 NSLog(@"使用NSArray添加3个元素后:%@" , NSCollectionToString(set)); 33 [set removeObject:@"疯狂XML讲义"]; 34 NSLog(@"删除1个元素后:%@" , NSCollectionToString(set)); 35 // 再次创建一个Set集合 36 NSSet* set2 = [NSSet setWithObjects: 37 @"孙悟空", @"疯狂iOS讲义" , nil]; 38 // 计算两个集合的并集,直接改变set集合的元素 39 [set unionSet: set2]; 40 // 计算两个集合的差集,直接改变set集合的元素 41 // [set minusSet: set2]; 42 // 计算两个集合的交集,直接改变set集合的元素 43 // [set intersectSet: set2]; 44 // 用set2的集合元素替换set的集合元素,直接改变set集合的元素 45 // [set setSet: set2]; 46 NSLog(@"%@" , NSCollectionToString(set)); 47 } 48 }
4、NSCountedSet是NSMutableSet的子类,与普通NSMutableSet集合不同的是,NSCountedSet为每个元素额外维护了一个添加次数的状态。当程序向NSCountedSet中添加一个元素时,如果NSCountedSet集合中不包含该元素,那么NSCountedSet真正接纳该元素,并将该元素的添加次数标注为1 ;当程序向NSCountedSet中添加一个元素时,如果NSCountedSet集合中已经包含该元素,那么NSCountedSet不会接纳该元素,但会将该元素的添加次数增加1 。
当程序从NSCountedSet中删除一个元素时,NSCountedSet只是将该元素对应的添加次数减1,只有当该元素的添加次数为0时,该元素才会真正从NSCountedSet集合中删除。
- countForObject::获取指定元素的添加次数
1 #import <Foundation/Foundation.h> 2 3 // 定义一个函数,该函数可把NSArray或NSSet集合转换为字符串 4 NSString* NSCollectionToString(id collection) 5 { 6 NSMutableString* result = [NSMutableString 7 stringWithString:@"["]; 8 // 使用快速枚举遍历NSSet集合 9 for(id obj in collection) 10 { 11 [result appendString:[obj description]]; 12 [result appendString:@", "]; 13 } 14 // 获取字符串长度 15 NSUInteger len = [result length]; 16 // 去掉字符串最后的两个字符 17 [result deleteCharactersInRange:NSMakeRange(len - 2, 2)]; 18 [result appendString:@"]"]; 19 return result; 20 } 21 int main(int argc , char * argv[]) 22 { 23 @autoreleasepool{ 24 NSCountedSet* set = [NSCountedSet setWithObjects: 25 @"疯狂iOS讲义" , @"疯狂Android讲义", 26 @"疯狂Ajax讲义" , nil]; 27 [set addObject:@"疯狂iOS讲义"]; 28 [set addObject:@"疯狂iOS讲义"]; 29 // 输出集合元素 30 NSLog(@"%@" , NSCollectionToString(set)); 31 // 获取指定元素的添加顺序 32 NSLog(@"\"疯狂iOS讲义\"的添加次数为:%ld" 33 , [set countForObject:@"疯狂iOS讲义"]); 34 // 删除元素 35 [set removeObject:@"疯狂iOS讲义"]; 36 NSLog(@"删除\"疯狂iOS讲义\"1次后的结果:%@" 37 , NSCollectionToString(set)); 38 NSLog(@"删除\"疯狂iOS讲义\"1次后的添加次数为:%ld" 39 , [set countForObject:@"疯狂iOS讲义"]); 40 // 重复删除元素 41 [set removeObject:@"疯狂iOS讲义"]; 42 [set removeObject:@"疯狂iOS讲义"]; 43 NSLog(@"删除\"疯狂iOS讲义\"3次后的结果:%@" 44 , NSCollectionToString(set)); 45 } 46 }
5、有序集合NSOrderedSet和NSMutableOrderedSet是两个非常nag奇怪的集合,他们既具有NSSet集合的特征,又具有NSArray类似的功能。(类似于Java中的LinkedHashSet集合,而NSSet就类似Java中的HashSet)
- NSOrderedSet不允许重复,这与NSSet集合相同
- NSOrderedSet可以保持元素的添加顺序,而且每个集合都有索引,可以根据索引来操作元素,这与NSArray相同
1 #import <Foundation/Foundation.h> 2 3 // 定义一个函数,该函数可把NSArray或NSSet集合转换为字符串 4 NSString* NSCollectionToString(id collection) 5 { 6 NSMutableString* result = [NSMutableString 7 stringWithString:@"["]; 8 // 使用快速枚举遍历NSSet集合 9 for(id obj in collection) 10 { 11 [result appendString:[obj description]]; 12 [result appendString:@", "]; 13 } 14 // 获取字符串长度 15 NSUInteger len = [result length]; 16 // 去掉字符串最后的两个字符 17 [result deleteCharactersInRange:NSMakeRange(len - 2, 2)]; 18 [result appendString:@"]"]; 19 return result; 20 } 21 int main(int argc , char * argv[]) 22 { 23 @autoreleasepool{ 24 // 创建NSOrderedSet集合,故意使用重复的元素 25 // 程序看到程序只会保留一个元素 26 NSOrderedSet* set = [NSOrderedSet orderedSetWithObjects: 27 [NSNumber numberWithInt:40], 28 [NSNumber numberWithInt:12], 29 [NSNumber numberWithInt:-9], 30 [NSNumber numberWithInt:28], 31 [NSNumber numberWithInt:12], 32 [NSNumber numberWithInt:17], 33 nil]; 34 NSLog(@"%@" , NSCollectionToString(set)); 35 // 下面方法都是根据索引来操作集合元素 36 // 获取第一个元素 37 NSLog(@"set集合的第一个元素 :%@" , [set firstObject]); 38 // 获取最后一个元素 39 NSLog(@"set集合的第一个元素 :%@" , [set lastObject]); 40 // 获取指定索引处的元素 41 NSLog(@"set集合中索引为2的元素 :%@" , [set objectAtIndex:2]); 42 NSLog(@"28在set集合中的索引为:%ld" , [set indexOfObject: 43 [NSNumber numberWithInt:28]]); 44 // 对集合进行过滤,获取元素值大于20的集合元素的索引 45 NSIndexSet* indexSet = [set indexesOfObjectsPassingTest: 46 ^(id obj, NSUInteger idx, BOOL *stop) 47 { 48 return (BOOL)([obj intValue] > 20); 49 }]; 50 NSLog(@"set集合中元素值大于20的元素的索引为:%@" , indexSet); 51 } 52 }
NSMutableOrderedSet是NSOrderedSet的子类,代表集合元素可变的有序集合。NSMutableOrderedSet在NSOrderedSet的基础上增加了添加元素、删除元素、替换元素、集合排序,以及计算集合的交、并、差等功能。
三、字典(NSDictionary和NSMutableDictionary)
1、NSDictoinary用于保存具有映射关系的数据,因此NSDictoinary集合中保存这两组值,一组用于保存NSDictoinary中的key,另一组用于保存NSDictoinary中的value。注意,key和value都可以是任意指针类型的数据,NSDictoinary中的key不允许重复。key和value之间存在单向一对一关系,即通过指定的key总能找到唯一的确定的value。从NSDictoinary中取出数据时,只要给定指定的key,就可以取出对应的value。
如果将NSDictoinary中的key和value分开来看,NSDictoinary所有的key放在一起,他们就组成了一个NSSet集合,所有的key没有顺序,key与key不能重复。实际上,NSDictoinary中包含了一个allKeys方法,只是该方法的返回值是NSArray类型,其实是该方法内部已经将NSSet集合进一步转化为NSArray了。
NSDictoinary的常用方法就不在这里进行列举了,需要的可以自行查看相关的参考文档 NSDictionary文档
NSDictionary、NSMutableDictionary的基本用法
1 #import <Foundation/Foundation.h> 2 #import "NSDictionary+print.h" 3 #import "FKUser.h" 4 5 int main(int argc , char * argv[]) 6 { 7 @autoreleasepool{ 8 // 直接使用多个value,key的形式创建NSDictionary对象 9 NSDictionary* dict = [NSDictionary 10 dictionaryWithObjectsAndKeys: 11 [[FKUser alloc] initWithName:@"sun" 12 pass:@"123"], @"one", 13 [[FKUser alloc] initWithName:@"bai" 14 pass:@"345"], @"two", 15 [[FKUser alloc] initWithName:@"sun" 16 pass:@"123"], @"three", 17 [[FKUser alloc] initWithName:@"tang" 18 pass:@"178"], @"four", 19 [[FKUser alloc] initWithName:@"niu" 20 pass:@"155"], @"five" , nil]; 21 [dict print]; 22 NSLog(@"dict包含%ld个key-value对", [dict count]); 23 NSLog(@"dict的所有key为:%@" , [dict allKeys]); 24 NSLog(@"<FKUser[name=sun,pass=123]>对应的所有key为:%@" 25 , [dict allKeysForObject: 26 [[FKUser alloc] initWithName:@"sun" 27 pass:@"123"]]); 28 // 获取遍历dict所有value的枚举器 29 NSEnumerator* en = [dict objectEnumerator]; 30 NSObject* value; 31 // 使用枚举器来遍历dict中所有value。 32 while(value = [en nextObject]) 33 { 34 NSLog(@"%@" , value); 35 } 36 // 使用指定代码块来迭代执行该集合中所有key-value对。 37 [dict enumerateKeysAndObjectsUsingBlock: 38 // 该集合包含多个key-value对,下面代码块就执行多少次 39 ^(id key, id value, BOOL *stop) 40 { 41 NSLog(@"key的值为:%@" , key); 42 [value say:@"疯狂iOS讲义"]; 43 }]; 44 } 45 }
对NSDictoinary的所有key进行排序,与NSArray的三种排序方法类似:
- keysSortedByValueUsingSelector::根据NSDictoinary的所有value的指定方法的返回值对key进行排序,调用value的该方法必须返回NSOrderedDescending、NSOrderedAscending、NSOrderedSame这些枚举值,用以代表集合元素的大小。
- keysSortedByValueUsingComparator:该方法使用指定的代码块来遍历key-value对,并根据执行结果对NSDictoinary的所有key进行排序。该代码块必须返回NSOrderedDescending、NSOrderedAscending、NSOrderedSame这些枚举值,用以代表集合元素的大小。
- keysSortedByValueWithOptions:usingComparator::与上一个方法功能类似,只是该方法可以传入一个额外的NSEnumeratorOptions参数。
1 #import <Foundation/Foundation.h> 2 #import "NSDictionary+print.h" 3 4 int main(int argc , char * argv[]) 5 { 6 @autoreleasepool{ 7 // 直接使用多个value,key的形式创建NSDictionary对象 8 NSDictionary* dict = [NSDictionary 9 dictionaryWithObjectsAndKeys: 10 @"Objective-C" , @"one", 11 @"Ruby" , @"two", 12 @"Python" , @"three", 13 @"Perl" , @"four", nil]; 14 // 打印dict集合的所有元素 15 [dict print]; 16 // 获取所有直接调用value的compare:方法对所有key进行排序。 17 // 返回排好序的所有key组成的NSArray。 18 NSArray* keyArr1 = [dict keysSortedByValueUsingSelector: 19 @selector(compare:)]; 20 NSLog(@"%@" , keyArr1); 21 NSArray* keyArr2 = [dict keysSortedByValueUsingComparator: 22 // 对NSDictionary的value进行比较,字符串越长,即可认为该value越大 23 ^(id value1, id value2) 24 { 25 // 下面定义比较大小的标准:字符串越长,即可认为value越大 26 if([value1 length] > [value2 length]) 27 { 28 return NSOrderedDescending; 29 } 30 if([value1 length] < [value2 length]) 31 { 32 return NSOrderedAscending; 33 } 34 return NSOrderedSame; 35 }]; 36 NSLog(@"%@" , keyArr2); 37 // 将NSDictionary的内容输出到指定文件中 38 [dict writeToFile:@"mydict.txt" atomically:YES]; 39 } 40 }
NSDictionary还提供了方法可以对NSDictionary的所有的key执行过滤,这些方法执行完后将返回满足过滤条件的key组成NSSet。
- keysOfEntriesPassingTest::使用代码块迭代处理NSDictionary中的每一个key-value对,对NSDictionary中的key-value进行过滤,该代码块必须返回BOOL类型的值,只有当代码块返回YES时,该key才会被保留下来,该代码块接受三个参数,其中第一个参数代表正在处理的key,第二个参数代表正在处理的value,第三个参数代表是否还需要继续迭代,如果第三个参数设置为NO则迭代停止。
- keysOfEntriesWithOptions:passingTest::该方法的功能与上一个方法一样,只是该方法可以传入一个额外的NSEnumeratorOptions参数。
1 #import <Foundation/Foundation.h> 2 #import "NSDictionary+print.h" 3 4 int main(int argc , char * argv[]) 5 { 6 @autoreleasepool{ 7 // 直接使用多个value,key的形式创建NSDictionary对象 8 NSDictionary* dict = [NSDictionary 9 dictionaryWithObjectsAndKeys: 10 [NSNumber numberWithInt:89] , @"Objective-C", 11 [NSNumber numberWithInt:69] , @"Ruby", 12 [NSNumber numberWithInt:75] , @"Python", 13 [NSNumber numberWithInt:109] , @"Perl", nil]; 14 // 打印dict集合的所有元素 15 [dict print]; 16 // 对NSDictionary的所有key进行过滤 17 NSSet* keySet = [dict keysOfEntriesPassingTest: 18 // 对NSDictionary的value进行比较,字符串越长,即可认为该value越大 19 ^(id key, id value, BOOL* stop) 20 { 21 // 当value的值大于80时返回YES 22 // 这意味着只有value的值大于80的key才会被保存下来 23 return (BOOL)([value intValue] > 80); 24 }]; 25 NSLog(@"%@" , keySet); 26 } 27 }
如果使用自定义的类作为NSDictionary的key,则该自定义类需要满足如下条件:
- 该自定义类正确重写了isEqual:和hash方法,所谓正确重写,是指当两个对象通过isEqual:判断相等时,这两个对象的hash方法的返回值也应该是相等的。
- 该自定义类必须实现copyWithZone:方法,该方法最好返回该对象的不可变副本。
1 #import "FKUser.h" 2 3 @implementation FKUser 4 @synthesize name; 5 @synthesize pass; 6 - (id) initWithName:(NSString*) aName 7 pass:(NSString*) aPass 8 { 9 if(self = [super init]) 10 { 11 name = aName; 12 pass = aPass; 13 } 14 return self; 15 } 16 - (void) say:(NSString*) content 17 { 18 NSLog(@"%@说:%@",self.name , content); 19 } 20 // 会重写isEqual:方法,重写该方法的比较标准是, 21 // 如果两个FKUser的name、pass相等,即可认为两个FKUser相等。 22 - (BOOL) isEqual:(id)other 23 { 24 if(self == other) 25 { 26 return YES; 27 } 28 if([other class] == FKUser.class) 29 { 30 FKUser* target = (FKUser*)other; 31 return [self.name isEqualToString:target.name] 32 && [self.pass isEqualToString:target.pass]; 33 } 34 return NO; 35 } 36 // 会重写isEqual:方法,重写该方法的比较标准是, 37 // 如果两个FKUser的name、pass相等,即可认为两个FKUser相等。 38 - (NSUInteger) hash 39 { 40 NSUInteger nameHash = name == nil ? 0 : [name hash]; 41 NSUInteger passHash = pass == nil ? 0 : [pass hash]; 42 return nameHash * 31 + passHash; 43 44 45 } 46 // 重写description方法,可以直接看到FKUser对象的状态 47 - (NSString*) description 48 { 49 return [NSString stringWithFormat: 50 @"<FKUser[name=%@, pass=%@]>" 51 , self.name , self.pass]; 52 } 53 - (id)copyWithZone:(NSZone *)zone 54 { 55 NSLog(@"--正在复制--"); 56 // 复制一个对象 57 FKUser* newUser = [[[self class] allocWithZone:zone] init]; 58 // 将被复制对象的实变量的值赋给新对象的实例变量 59 newUser->name = name; 60 newUser->pass = pass; 61 return newUser; 62 } 63 @end
1 #import <Foundation/Foundation.h> 2 #import "NSDictionary+print.h" 3 #import "FKUser.h" 4 5 int main(int argc , char * argv[]) 6 { 7 @autoreleasepool{ 8 FKUser* u1 = [[FKUser alloc] initWithName:@"bai" 9 pass:@"345"]; 10 // 直接使用多个value,key的形式创建NSDictionary对象 11 NSDictionary* dict = [NSDictionary 12 dictionaryWithObjectsAndKeys: 13 @"one", [[FKUser alloc] initWithName:@"sun" 14 pass:@"123"], 15 @"two", u1, 16 @"three",[[FKUser alloc] initWithName:@"sun" 17 pass:@"123"], 18 @"four",[[FKUser alloc] initWithName:@"tang" 19 pass:@"178"], 20 @"five" ,[[FKUser alloc] initWithName:@"niu" 21 pass:@"155"], nil]; 22 // 将u1的密码设为nil 23 u1.pass = nil; 24 // 由于NSDictionary并未直接使用u1所指向的FKUser作为key, 25 // 而是先复制了u1所指向对象的副本,然后以该副本作为key。 26 // 因此程序将可以看到dict的key不会受到任何影响。 27 [dict print]; 28 } 29 }
2、NSMutableDictionary是NSDictionary的子类,它代表一个key-value可变的NSDictionary字典集合。同样由于NSMutableDictionary可以动态地添加key-value对,因此,创建NSMutableDictionary集合时可以指定初始容量。类似NSMutableArray与NSArray的关系,NSMutableDictionary主要在NSDictionary的基础上增加了添加、删除key-value对的方法。具体参见 NSMutableDictionary文档
1 #import <Foundation/Foundation.h> 2 #import "NSDictionary+print.h" 3 4 int main(int argc , char * argv[]) 5 { 6 @autoreleasepool{ 7 // 直接使用多个value,key的形式创建NSDictionary对象 8 NSMutableDictionary* dict = [NSMutableDictionary 9 dictionaryWithObjectsAndKeys: 10 [NSNumber numberWithInt:89] , @"疯狂Android讲义", nil]; 11 // 使用下标法设置key-value对。 12 // 由于NSDictionary中已存在该key, 13 // 因此此处设置的value会覆盖前面的value。 14 dict[@"疯狂Android讲义"] = [NSNumber numberWithInt:99]; 15 [dict print]; 16 NSLog(@"--再次添加key-value对--"); 17 dict[@"疯狂XML讲义"] = [NSNumber numberWithInt:69]; 18 [dict print]; 19 NSDictionary* dict2 = [NSDictionary 20 dictionaryWithObjectsAndKeys: 21 [NSNumber numberWithInt:79] , @"疯狂Ajax讲义", 22 [NSNumber numberWithInt:89] , @"Struts 2.x权威指南" 23 , nil]; 24 // 将另外一个NSDictionary中的key-value对添加到当前NSDictionary中 25 [dict addEntriesFromDictionary:dict2]; 26 [dict print]; 27 // 根据key来删除key-value对 28 [dict removeObjectForKey:@"Struts 2.x权威指南"]; 29 [dict print]; 30 } 31 }