OC------集合概述

目录

7.4 Objective-C集合概述           

7.5 数组(NSArray与NSMutableArray) 

NSarray的功能与用法

对集合元素整体调用方法

对NSArray进行排序

使用枚举器遍历NSArray集合元素

快速枚举(for...in)

 可变数组(NSMutableArray)

NSArray的KVC与KFC

7.6集合(NSSet和NSMutableSet)

NNSet的功能与用法 

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

 NSMutableSet的功能与用法

 NSCountedSet的功能与用法

有序集合(NSOrederedSet和NSMutableOrderedSet) 

7.7字典(NSDictionary和NSMutableDictionary) 

 NSDictionary的功能与用法

对 NSDictionary的key排序

对 NSDictionary的key进行过滤

使用自定义类作为NSDictionary的key 

 NSMutableDictionary的功能与用法



7.4 Objective-C集合概述           

       OC的集合大致上可以分为:NSArray、NSSet 和 NSDictionary三种体系,NSArray代表有序、可以重复的集合;NSSet代表无序、不可重复的集合;NSDictionary则代表具有映射关系的集合。
       为了保存数量不确定的数据,以及保存具有映射关系的数据(关联数组),OC提供了集合类。
       集合类和数组不同,数组元素既可以是基本类型的值,也可以是对象(实际保存的是对象的指针变量);而集合里只能保存对象(实际上也是保存对象的指针变量)


       NSSet集合类似于一个罐子,NSSet集合无法记住添加这个元素的顺序,所以NSSet里的元素不能重复;NSArray集合非常像一个数组,它可以记住每次添加元素的顺序,NSMutableArray的长度可变。NSDictionary集合也像一个罐子,只是它里面的每项数据都由两个值组成。
 

从图中可以看出,如果访问NSArray集合中的元素,则可以直接根据元素的索引来访问;如果需要访问NSDictionary集合中的元素,则可以根据每项元素的key值来访问其value;如果需要访问NSSet集合中的元素,则只能根据元素本身来访问。

7.5 数组(NSArray与NSMutableArray) 

NSArray代表元素有序、可重复的一个集合,集合中的每个元素都有其对应的顺序索引。

NSarray的功能与用法

NSArray提供了类方法与实例方法来创建NSArray,两种创建方式需要传入的参数基本相似,只是类方法以array开头,示例方法以init开头。下面介绍几个常见的方法:

array:创建一个不包含任何元素的空NSArray。
arrayWithContentsOfFile:/initWithContentsOfFile::读取文件内容来创建NSArray。
arrayWithObject:/initWithObject::创建只包含指定元素的NSArray。
arrayWithObjects:/initWithObjects::创建包含指定N个元素的NSArray。

此外,还可以使用如下简化语法来创建NSArray对象:

@[元素1, 元素2, 元素3, ...]

参考NSArray类的文档,可以看到NSArray集合的方法大致包含如下几类:

  • 查询集合元素在NSArray中的索引。
  • 根据索引值取出NSArray集合中的元素。
  • 对集合元素整体调用方法。
  • 对NSArray集合进行排序。
  • 取出NSArray集合中的部分集合组成新集合。
#import <Foundation/Foundation.h>

int main(int argc, const char * argv[]) {
    @autoreleasepool {
        NSArray* array = [NSArray arrayWithObjects:@"疯狂iOS讲义", @"疯狂Android讲义",@"疯狂ajax讲义", @"疯狂MXL讲义", @"Struct 2.x指南", nil];
        NSLog(@"第一个元素:%@", [array objectAtIndex: 0]);
        NSLog(@"索引为1元素:%@", [array objectAtIndex: 1]);
        NSLog(@"最后一个元素:%@", [array lastObject]);
        NSArray* arr1 = [array objectsAtIndexes:[NSIndexSet indexSetWithIndexesInRange:NSMakeRange(2, 3)]];
        
        NSLog(@"%@", arr1);
        for (int i = 0; i < arr1.count; i++) {
            NSLog(@"%@", [arr1 objectAtIndex:i]);
            NSLog(@"%@", arr1[i]);
        }
        NSLog(@"疯狂Android讲义的位置为:%ld", [array indexOfObject:@"疯狂Android讲义"]);
        NSLog(@"在2-5范围疯狂Android讲义的位置为:%ld", [array indexOfObject:@"疯狂Android讲义"inRange:NSMakeRange(2, 3)]);
        array = [array arrayByAddingObject:@"孙悟空"];
        array = [array arrayByAddingObjectsFromArray:[NSArray arrayWithObjects:@"宝玉", @"黛玉", nil]];
        for (int i = 0; i < array.count; i++) {
            NSLog(@"%@", [array objectAtIndex:i]);
            NSLog(@"%@", array[i]);
        }
        NSArray* arr2 = [array subarrayWithRange:NSMakeRange(5, 3)];
        [arr2 writeToFile:@"myfile.txt" atomically:YES];
        
    }
    return 0;
}
第一个元素:疯狂iOS讲义
索引为1元素:疯狂Android讲义
最后一个元素:Struct 2.x指南
2023-05-14 14:46:22.459173+0800 NSArray的功能与用法[92513:5393752] (
    "\U75af\U72c2ajax\U8bb2\U4e49",
    "\U75af\U72c2MXL\U8bb2\U4e49",
    "Struct 2.x\U6307\U5357"
)
疯狂ajax讲义
疯狂MXL讲义
Struct 2.x指南
疯狂Android讲义的位置为:1
2在2-5范围疯狂Android讲义的位置为:9223372036854775807
疯狂iOS讲义
疯狂Android讲义
疯狂ajax讲义
疯狂MXL讲义
Struct 2.x指南
孙悟空
宝玉
黛玉

创建NSArray对象时可以直接传入多个元素,其中最后一个nil表示NSArray元素结束,它并不会存入NSArray集合。

程序遍历NSArray集合时,既可通过调用NSArray的objectAtlndex:index方法来访问指定索引处的元素,也可直接使用类似于普通数组的下标表示法来访问元素。也就是说,如下两条代码的作用是相同的。

[array objectAtIndex:i];
array[i];

上述程序中9223372036854775807是常量NSNotFound的值。
程序使用了两种方法向NSArray集合后面追加了元素:

使用arrayByAddingObject:方法追加单个元素
使用arrayWithObjects:方法将另一个数组中的所有元素追加到原数组的后面。

 NSArray判断指定元素位于NSArray集合中的索引的标准只有一条:只有某个集合元素与被查找元素通过isEqual: 方法比较返回YES时,才可以认为该NSArray集合包含该元素,并不需要两个元素是同一个元素。

FKUser.h

#import <Foundation/Foundation.h>

NS_ASSUME_NONNULL_BEGIN

@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

NS_ASSUME_NONNULL_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相等,即可认为他们相等
- (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;
}
//重写description方法,可以直接看到FKUser对象的状态
- (NSString*) description {
    return [NSString stringWithFormat:@"<FKUser[name = %@, pass = %@]>", self.name, self.pass];
}
@end

main.m

#import <Foundation/Foundation.h>
#import "FKUser.h"
int main(int argc, const char * argv[]) {
    @autoreleasepool {
        //初始化NSArray对象
        NSArray* array = [NSArray arrayWithObjects:
                          [[FKUser alloc] initWithName:@"sun" pass:@"123"],
                          [[FKUser alloc] initWithName:@"bai" pass:@"345"],
                          [[FKUser alloc] initWithName:@"zhu" pass:@"654"],
                          [[FKUser alloc] initWithName:@"tang" pass:@"178"],
                          [[FKUser alloc] initWithName:@"niu" pass:@"155"],nil];
        FKUser* newUser = [[FKUser alloc] initWithName:@"zhu" pass:@"654"];
        //查找新FKUser对象在集合中的索引
        NSInteger* pos = [array indexOfObject:newUser];
        NSLog(@"newUser的位置为%ld", pos);
    return 0;
}

很明显,这是一个全新的FKUser对象,虽然该对象与集合中的任何对象都不相同,但程序依然可以返回该FKUser对象在该集合中的索引,这是因为该FKUser对象的name、pass与集合中第3个元素的name、pass相等,因此,程序返回这个新的FKUser对象在集合中的索引应为2。

对集合元素整体调用方法


NSArray允许对集合中所有的元素或部分元素整体调用方法,如果只是简单地调用集合元素的方法,则可以通过NSArray的如下两种方法来实现。

makeObjectsPerformSelector::依次调用NSArray集合中每个元素的指定方法,该方法需要传入一个SEL参数,用于指定调用哪个方法。
makeObjectsPerformSelector:withObject::依次调用NSArray集合中每个元素的指定方法,该方法第一个SEL参数用于指定调用哪个方法,第二个参数用于调用集合元素的方法时传入参数。


如果希望对集合中的所有元素进行隐式遍历,并使用集合元素来执行某段代码,则可以通过NSArray的如下方法来完成。

enumerateObjectsUsingBlock::遍历集合中的所有元素,并依次使用元素来执行指定的代码块。
enumerateObjectWithOptions:usingBlock::遍历集合中的所有元素,并依次使用元素来执行指定的代码块。该方法可以额外传入一个参数,用于控制遍历的选项,如反向遍历。
enumerateObjectsAtIndexes:options:usingBlock::遍历集合中指定范围内的元素,并依次使用元素来执行指定的代码块。该方法可以额外传入一个参数,用于控制遍历的选项,如反向遍历。


上面3个方法都需要传入一个代码块参数,该代码块必须带3个参数,第一个参数代表正在遍历的集合元素,第二个参数代表正在遍历的集合元素的索引,第三个参数就是用于遍历集合元素的代码块。

#import <Foundation/Foundation.h>
#import "FKUser.h"
int main(int argc, const char * argv[]) {
    @autoreleasepool {
        //初始化NSArray对象
        NSArray* array = [NSArray arrayWithObjects:
                          [[FKUser alloc] initWithName:@"sun" pass:@"123"],
                          [[FKUser alloc] initWithName:@"bai" pass:@"345"],
                          [[FKUser alloc] initWithName:@"zhu" pass:@"654"],
                          [[FKUser alloc] initWithName:@"tang" pass:@"178"],
                          [[FKUser alloc] initWithName:@"niu" pass:@"155"],nil];
        FKUser* newUser = [[FKUser alloc] initWithName:@"zhu" pass:@"654"];
        //对集合元素整体调用方法
        [array makeObjectsPerformSelector:@selector(say:) withObject:@"NSArray真强大"];
        NSString* content = @"疯狂iOS讲义";
        //迭代集合内指定范围内的元素,并使用该元素来执行代码块
        [array enumerateObjectsAtIndexes:[NSIndexSet indexSetWithIndexesInRange:NSMakeRange(2, 2)]
            options:NSEnumerationReverse//代码块的第一个参数代表正在遍历的集合元素,第二个参数为这个集合元素的索引
            usingBlock:^(id obj, NSUInteger idx, BOOL* stop) {
            NSLog(@"正在处理的%ld个元素:%@", idx, obj);
            [obj say:content];
        }];
    }
    return 0;
}

 NSArray既可以对集合中所有元素整体调用某个方法,也可以通过NSIndexSet来控制只对集合中部分元素迭代调用指定的代码块。虽然程序只是调用NSArray的方法,但底层实际上会使用循环进行迭代处理调用每个集合元素,因此,这些方法都可以称为迭代方法

对NSArray进行排序


NSArray提供了大量的方法对集合元素进行排序,这些排序方法都以sorted开头,最常用的排序方法如下:

sortedArrayUsingFunction:context::使用排序函数对集合元素进行排序,函数必须返回NSOrderedDescending、NSOrderedAscending、NSorderedSame这些枚举值,用于代表集合元素的大小。该方法返回一个排序好的新NSArray对象。
sortedArrayUsingSelector::使用集合元素自身的方法对集合元素进行排序,函数必须返回NSOrderedDescending、NSOrderedAscending、NSorderedSame这些枚举值,用于代表集合元素的大小。该方法返回一个排序好的新NSArray对象。
sortedArrayUsingComparator::使用代码块对集合元素进行排序,函数必须返回NSOrderedDescending、NSOrderedAscending、NSorderedSame这些枚举值,用于代表集合元素的大小。该方法返回一个排序好的新NSArray对象。

实际上sortedArrayUsingComparator:方法时sortedArrayUsingFunction:context:方法的简化版本。 

#import <Foundation/Foundation.h>
NSInteger intSort(id num1, id num2, void* context) {
    int  v1 = [num1 intValue];
    int v2 = [num2 intValue];
    if (v1 < v2) {
        return NSOrderedAscending;
    } else if (v1 > v2) {
        return NSOrderedDescending;
    } else {
        return NSOrderedSame;
    }
}
int main(int argc, const char * argv[]) {
    @autoreleasepool {
        //初始化一个元素为NSString的NSAraay对象
        NSArray* array1 = [NSArray arrayWithObjects:@"OC", @"C", @"C++", @"Ruby", @"perl", @"Pythen", nil];
        //使用集合元素自身的compare:方法进行排序
        array1 = [array1 sortedArrayUsingSelector:@selector(compare:)];
        NSLog(@"%@", array1);
        //初始化一个元素为NSNumber的NSAraay对象
        NSArray* array2 = [NSArray arrayWithObjects:
                           [NSNumber numberWithInt:20],
                           [NSNumber numberWithInt:12],
                           [NSNumber numberWithInt:-1],
                           [NSNumber numberWithInt:50],
                           [NSNumber numberWithInt:13],nil];
        //使用自定义的intsort方法进行排序
        array2 = [array2 sortedArrayUsingFunction:intSort context:nil];
        NSLog(@"%@", array2);
        //使用代码块的intsort方法进行排序
        NSArray* array3 = [array2 sortedArrayUsingComparator:^(id obj1, id obj2) {
            if ([obj1 intValue] < [obj2 intValue]) {
                return NSOrderedAscending;
            } else if ([obj1 intValue] > [obj2 intValue]) {
                return NSOrderedDescending;
            } else {
                return NSOrderedSame;
            }
        }];
        NSLog(@"%@", array3);
    }
    return 0;
}

集合元素本身可以比较大小,而且直接利用集合元素比较大小的方法进行排序的方法称为自然排序;对于通过比较函数或代码块来指定自定义比较规则的方式,则被称为定制排序。

使用枚举器遍历NSArray集合元素


除了根据集合元素的索引来遍历元素,还可以调用NSArray对象的如下两个方法来返回枚举器。

objectEnumerator :返回NSArray集合的顺序枚举器。
reverseObjectEnumerator :返回NSArray集合的逆序枚举器。

上面两个方法都返回一个NSEnumerator枚举器,该枚举器只包含如下两个方法:

allObjects:获取被枚举集合中的所有元素。
nextObject:获取被枚举集合中的下一个元素。

一般来说,借助nextObject方法即可对集合元素进行枚举:程序可以采用循环不断获取nextObject方法的返回值,直到该方法的返回值为nil结束循环。
 

#import <Foundation/Foundation.h>

int main(int argc, const char * argv[]) {
    @autoreleasepool {
        NSArray* array = [NSArray arrayWithObjects:
                          [NSNumber numberWithInt:20],
                          [NSNumber numberWithInt:12],
                          [NSNumber numberWithInt:-1],
                          [NSNumber numberWithInt:50],
                          [NSNumber numberWithInt:13],nil];
        NSEnumerator* en = [array objectEnumerator];
        id object;
        while (object = [en nextObject]) {
            NSLog(@"%d", [object intValue]);
        }
        NSLog(@"---下面逆序遍历---");
        en = [array reverseObjectEnumerator];
        while (object = [en nextObject]) {
            NSLog(@"%@", object);
        }
    }
    return 0;
}

快速枚举(for...in)

 OC还提供了一种快速枚举的方法来遍历集合(包括NSArray,NSSet, NSDaictionary的集合),使用快速枚举时无须获得集合的长度,也无须根据索引来访问集合元素。

语法格式:

for (type variableName in collection) {
       //variableName 自动迭代访问每个元素
}

type是集合元素的类型,variableName是一个形参名,快速枚举将自动将集合元素依次赋给该变量。

int main(int argc, const char * argv[]) {
    @autoreleasepool {
        NSArray* array = [NSArray arrayWithObjects:
                          [NSNumber numberWithInt:20],
                          [NSNumber numberWithInt:12],
                          [NSNumber numberWithInt:-1],
                          [NSNumber numberWithInt:50],
                          [NSNumber numberWithInt:13],nil];
        for (id object in array) {
            NSLog(@"%@", object);
        }
    }
    return 0;
}

快速枚举的本质是一个for-each循环,for-each循环和普通循环不同的是,它无须循环条件,也无须循环迭代语句,这些部分都由系统来完成,for-each循环自动迭代数组的每个元素,当每个元素都被迭代一次后,for-each循环自动结束。

 可变数组(NSMutableArray)

NSArray有一个子类:NSMutableArray,因此它可作为NSArray使用。与此同时,它代表的是一个集合元素可变的集合,因此,程序可以向集合中添加新的元素,可以删除集合中已有的元素,也可以替换集合元素。
NSMutableArray代表集合元素可变的集合,而NSMutableArray底层采用传统数组来容纳集合元素,因此,创建NSMutableArray时可通过参数指定底层数组的初始容量。
NSMutableArray主要新增了如下方法。

添加集合元素的方法:这类方法以add开头。
删除集合元素的方法:这类方法以remove开头。
替换集合元素的方法:这类方法以replace开头。
对集合本身排序的方法:这类方法以sort开头。

NSMutableAray同样提供了sortUsingSelector:、sortUsingComparator:、sortUsingFunction:
context:这三个方法,这三个方法与前面介绍的NSArray提供的三个排序方法的用法基本相似,区别是NSArray的排序方法是返回一个新的、排序好的NSArray对象,而NSMutableArray的排序方法则对集合本身排序

#import <Foundation/Foundation.h>
NSString* NSCollectionToString(NSArray* 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, const char * argv[]) {
    @autoreleasepool {
        NSMutableArray* array = [NSMutableArray arrayWithObjects:@"孙悟空", @"宝玉", @"黛玉",  nil];
        [array addObject:@"疯狂iOS讲义"];
        NSLog(@"追加最后一个元素后:%@",  NSCollectionToString(array));
        [array addObjectsFromArray:[NSArray arrayWithObjects:@"张飞", @"关羽", nil]];
        NSLog(@"追加最后两个元素后:%@",  NSCollectionToString(array));
        [array insertObject:@"疯狂Ajax讲义" atIndex:2];
        NSLog(@"在索引为2处插入一个元素后:%@", NSCollectionToString(array));
        [array insertObjects:[NSArray arrayWithObjects:@"武松", @"林冲", nil] atIndexes:[NSIndexSet indexSetWithIndexesInRange:NSMakeRange(3, 2)]];
        NSLog(@"插入多个元素后:%@",  NSCollectionToString(array));
        [array removeLastObject];
        NSLog(@"删除最后一个元素后:%@",  NSCollectionToString(array));
        [array removeObjectAtIndex:5];
        NSLog(@"删除索引为5的元素后:%@",  NSCollectionToString(array));
        [array removeObjectsInRange:NSMakeRange(2, 3)];
        NSLog(@"删除索引为2-5的元素后:%@",  NSCollectionToString(array));
        [array replaceObjectAtIndex:2 withObject:@"疯狂Android"];
        NSLog(@"替换索引为2的元素后:%@",  NSCollectionToString(array));
        
    }
    return 0;
}

NSArray的KVC与KFC

NSArray是一个容纳多个对象的集合,NSArray允许直接对集合中的所有元素进行整体的KVC编码。NSArray提供了如下两个方法。


setValue:forKey::将NSArray集合中所有元素的指定key对应属性或实例变量设置
为value。
valueForKey:返回该NSArray集合中所有元素的指定key组成的NSArray对象。

除此之外,NSArray还为对集合中的所有元素或部分元素进行KVO编程提供了如下方法。

addObserver:forKeyPath:options:context::为集合中的所有元素添加KVO监听器。removeObserver:forKeyPath::为集合中的所有元素删除KVO监听器。
addObserver:toObjectsAtIndexes:forKeyPath:options:context::为集合中指定索引处的元素添加KVO监听器。
removeObserver:fromObjectsAtlndexes:forKeyPath::为集合中指定索引处的元素删除KVO监听器。

#import <Foundation/Foundation.h>
#import "FKUser.h"
NSString* NSCollectionToString(NSArray* 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, const char * argv[]) {
    @autoreleasepool {
        NSArray* array = [NSArray arrayWithObjects:
                          [[FKUser alloc] initWithName:@"sun" pass:@"123"],
                          [[FKUser alloc] initWithName:@"bai" pass:@"345"],
                          [[FKUser alloc] initWithName:@"zhu" pass:@"654"],
                          [[FKUser alloc] initWithName:@"tang" pass:@"178"],
                          [[FKUser alloc] initWithName:@"niu" pass:@"155"],nil];
        //将所有集合元素的name属性组成新集合
        id newArr = [array valueForKey:@"name"];
        NSLog(@"%@",  NSCollectionToString(newArr));
        //对集合进行KVC编程
        //将所有集合元素的name属性改为“新名字”
        [array setValue:@"新名字" forKey:@"name"];
        NSLog(@"%@",  NSCollectionToString(array));
    }
    return 0;
}

第一行粗体字代码一次性将NSArray集合中所有元素的name属性取出来了;第二行粗体字代码则全部将NSArray集合中所有元素的name属性值都改成了新值。

7.6集合(NSSet和NSMutableSet)

NNSet的功能与用法 

NSSet集合里多个对象之间没有明显顺序,不允许包含相同的元素,如果试图把两个相同的元素放在同一个NSSet集合中,则只会保留一个元素NSSet按Hash算法来储存元素,因此具有良好的存取和查找功能。NSSet不能保证元素的添加顺序,顺序可能发生变化。与NSArray相比,NSSet最大的区别是元素没有索引,因此不能根据索引来操作元素

NSSet与NSArray依然有大量的相似之处:

都可通过count获取集合元素个数。

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

都可以通过objectEnumerator方法获取NSEnumerator枚举器对集合元素进行遍历,由于NSSet无序,因此,提供反向迭代器没有意义。

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

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

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

除了与NSArray相似的方法,NSSet包含了如下常用的方法:

setByAddingObject::向集合添加一个新元素,返回添加元素后的新集合。setByAddingObjectsFromSet::使用NSSet添加多个元素,返回添加元素后的新集合。setByAddingObjectsFromArray::使用NSArray添加多个元素,返回添加元素后的新集合。allObjects::返回该集合中所有元素组成的NSArray

anyObject::返回某个元素,元素是不确定的,但该方法并不保证随机返回(只要一个NSSet没有发生改变,无论调用多少次,返回的总是同一个元素)。

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

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

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

objectsWithOptions:passingTest::与前一个方法的功能基本相似,只是可以额外地传入一个NSEnumerationOptions迭代选项参数。

isSubsetOfSet::判断当前NSSet集合是否为另一个集合的子集合。调用该方法需要传入另一集合。

intersectsSet::判断两个集合的元素是否有相同元素。也就是计算两个集合是否有交集。isEqualToSet::判断两个集合的元素是否相等。 

#import <Foundation/Foundation.h>
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, const char * argv[]) {
    @autoreleasepool {
        NSSet* set1 = [NSSet setWithObjects:@"疯狂iOS讲义", @"疯狂Android讲义", @"疯狂Ajax讲义",  @"疯狂iOS讲义", nil];
        NSLog(@"set1集合元素个数为%ld", [set1 count]);
        NSLog(@"s1集合: %@",  NSCollectionToString(set1));
        NSSet* set2 = [NSSet setWithObjects:@"孙悟空", @"疯狂Android讲义", @"猪八戒", nil];
        NSLog(@"s2集合: %@",  NSCollectionToString(set2));
        set1 = [set1 setByAddingObject:@"权威指南"];
        NSLog(@"添加一个元素后s1集合: %@",  NSCollectionToString(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);
        BOOL bb = [set1 containsObject:@"疯狂Ajax讲义"];
        NSLog(@"set1是否包含疯狂Ajax讲义:%d", bb);
        //两行代码取出相同的元素,但不确定取出哪一个元素
        NSLog(@"set1取出一个元素:%@", [set1 anyObject]);
        NSLog(@"set1取出一个元素:%@", [set1 anyObject]);
        //使用代码块对集合元素进行过滤
        NSSet* newSet = [set1 objectsPassingTest:^(id obj, BOOL* stop) {
            return (BOOL)([obj length] > 8);
        }];
        NSLog(@"set1中长度大于8的元素:%@", NSCollectionToString(newSet));
        
    }
    return 0;
}

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

NSSet集合中存入一个元素,NSSet会调用该对象的hash方法来得到该对象的hashCode值,然后根据该hashCode值决定该对象在底层Hash表中的存储位置,如果位置不同,那么系统直接将它们保存在不同的位置。

如果两个元素的hashCode值相同,接下来通过isEqual:方法判断两个元素是否相等,若isEqual:方法返回NO,那么NSSet依然认为它们不相等,会把他们放在底层Hash表同一位置只是将在这个位置形成链;如果它们通过isEqual: 方法比较返回YES,那么认为两个元素相等,后面的元素添加失败 

main.m

#import <Foundation/Foundation.h>
#import "FKUser.h"
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, const 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));
    }
    return 0;
}

FKUser.h

#import <Foundation/Foundation.h>

NS_ASSUME_NONNULL_BEGIN

@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

NS_ASSUME_NONNULL_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相等,即可认为他们相等
- (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;
}
//重写description方法,可以直接看到FKUser对象的状态
- (NSString*) description {
    return [NSString stringWithFormat:@"<FKUser[name = %@, pass = %@]>", self.name, self.pass];
}
@end

 第1个和第三个FKuser对象通过isEqual方法会返回YES.但程序没有重写他们的Hash方法,两个FKuser的HashCode依然不相等,NSSet依然认为他们不相等,会同时存储两个元素。

为FKUser重写Hash方法后

-(NSUInteger) hash {
    NSLog(@"---hash---");
    NSUInteger nameHash = name == nil ? 0 : [name hash];
    NSUInteger passHash = pass == nil ? 0 : [pass hash];
    return nameHash * 31 + passHash;
    
}

上面程序计算FKUser的hash方法是根据name、pass的hash方法返回值进行的,因此只要两个FKUser的name、pass的hash方法返回值相等,那么两个FKUser对象的hash方法返回值就相等。NSSet认为这两个FKUser相等,NSSet只保留一个。
程序会输出“===hash===”5次,这表明NSSet每次添加一个集合元素,总会先调用该元素的hash方法——所有的对象都继承了NSObject类,因此,所有的对象都有hash方法。


如果需要把一个对象放入NSSet中,若重写该对象对应类的isEqual:方法,也应该重写其hash方法。其规则是:如果两个对象通过 isEqual:方法比较返回YES,那么这两个对象的hash方法的返回值也应该相同

重写Hash方法的基本规则

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

当两个对象通过isEqual:方法比较返回YES时,这两个对象的hash方法应返回相等的值。
对象中作为isEqual:方法比较标准的成员变量,都应该用来计算hashCode值。

下面给出重写hash方法的一般步骤。


1 把对象内每个有意义的成员变量(即每个用作 isEqual:方法比较标准的实例变量)计算出一个int类型的hashCode值。
2 用第1步计算出来的多个hashCode值组合计算出一个hashCode值返回。
return [fl hash] +[f2 hash];
为了避免直接相加产生偶然相等(两个对象的f1、f2实例变量并不相等,但它们的hashCode值和恰好相等),可以通过为各实例变量的hashCode值乘以任意一个质数后再相加。
return [f1 hash] * 31+ [f2 hash]; 

 NSMutableSet的功能与用法

NSMutable继承NSSet,代表一个集合元素可变的NSSet集合。由于NSMutableSet可以动态添加集合元素,所以。创建NSS et集合可指定底层Hash表的初始容量。

NSMutableSet在NSSet的基础上添加了以下方法

addObject:向集合中添加单个元素。
removeObject::从集合中删除单个元素。
removeAllObjects:删除集合中的所有元素。
addObjectsFromArray::使用NSAray数组作为参数,向NSSet集合中添加参数数组中的所有元素。
unionSet::计算两个NSSet集合的并集。
minusSet::计算两个NSSet集合的差集。
intersectSet::计算两个NSSet集合的交集。
setSet::用后一个集合的元素替换已有集合中所有的元素。

#import <Foundation/Foundation.h>
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, const char * argv[]) {
    @autoreleasepool {
        NSMutableSet* set = [NSMutableSet setWithCapacity:10];
        [set addObject:@"疯狂iOS讲义"];
        NSLog(@"添加一个元素后set集合: %@",  NSCollectionToString(set));
        [set addObjectsFromArray:[NSArray arrayWithObjects:@"疯狂Android讲义", @"疯狂Ajax讲义",  @"疯狂iOS讲义", nil]];
        NSLog(@"使用NSArray添加三个元素后:%@",  NSCollectionToString(set));
        [set removeObject:@"疯狂Ajax讲义"];
        NSLog(@"删除一个元素元素后:%@",  NSCollectionToString(set));
        NSSet* set2 = [NSSet setWithObjects:@"孙悟空", @"疯狂iOS讲义", nil];
        [set unionSet:set2];
        [set minusSet:set2];
        [set intersectSet:set2];
        [set setSet:set2];
        NSLog(@"%@", NSCollectionToString(set));
        
    }
    return 0;
}

 NSCountedSet的功能与用法

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

当程序向NSCountedSet中添加一个元素时,如果NSCouniedSet集合中不包含该元素,那么NSCountedSet真正接纳该元素,并将该元素的添加次数标注为1;当程序向NSCountedSet中添加一个元素时,如果NSCountedSet集合中已经包含该元素,那么NSCountedSet不会接纳该元素,但会将该元素的添加次数加1。当程序从NSCountedSet集合中删除元素时,NSCountedSet只是将该元素的添加次数减1,只有当该元素的添加次数变为0时,该元素才会真正从NSCountedSet集合中删除。


NSCountedSet提供了如下方法来返回某个元素的添加次数。
countForObject::获取指定元素的添加次数。 

#import <Foundation/Foundation.h>
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, 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:@"fenfkiOS讲义"]);
        [set removeObject:@"疯狂iOS讲义"];
        NSLog(@"删除一次后的结果:%@",NSCollectionToString(set));
        [set removeObject:@"疯狂iOS讲义"];
        NSLog(@"删除两次后的结果:%@",NSCollectionToString(set));
        [set removeObject:@"疯狂iOS讲义"];
        NSLog(@"删除三次后的结果:%@",NSCollectionToString(set));
    }
        return 0;
}

有序集合(NSOrederedSet和NSMutableOrderedSet) 

NSOrderedSet和NSMutableOrderedSet是两个非常奇怪的集合,它们既具有NSSet集合的特征,也具有NSArray类似的功能。

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

NSMutableOrderedSet增加了添加元素、删除元素、替换元素、集合排序,以及计算集合的交集、并集、差集等功能,与NSMutableSet为NSSet集合增加的功能相似。 

#import <Foundation/Foundation.h>
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, const char * argv[]) {
    @autoreleasepool {
       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);
        NSLog(@"set集合的第一个元素:%@", [set firstObject]);
        NSLog(@"set集合的最后一个元素:%@", [set lastObject]);
        NSLog(@"set集合索引为2的元素:%@", [set objectAtIndex:2]);
        NSLog(@"28在set集合的索引为%ld", [set indexOfObject:[NSNumber numberWithInt:28]]);
        NSInteger* indexSet = [set indexesOfObjectsPassingTest:^(id obj, NSUInteger idx,  BOOL* stop) {
            return; (BOOL) ([obj intValue] > 20);
        }];
        NSLog(@"set集合中元素大于20的集合元素%@", indexSet);
        
    }
    return 0;
}

7.7字典(NSDictionary和NSMutableDictionary) 

此,NSDictionary集合里保存着两组值,组值用于保存NSDictionary里的key,另一组值用于保存NSDictionary里的value。注意,key 和value都可以是任何指针类型的数据,NSDictionary的key不允许重复。
key和value之间存在单向一对一关系,即通过指定的key,总能找到唯一的、确定的value。从 NSDictionary中取出数据时,只要给出指定的key,就可以取出对应的value。

如果把NSDictionary里 key 数据组 value数据组的所有key放在一起,它们就组成一个NSSet集合
所有的key 没有顺序,key与key之间不能重复),和实际上,NSDictionary确实包含了一个allKeys方法, value组用于返回NSDictionary所有key组成的集合。遗憾的是,NSDictionary把allKeys方法的返回值设为NSArray——这说明该方法经过了进一步转换,它已经把NSSet集合转换成了NSArray集合。


 NSDictionary的功能与用法

NSDictionary分别提供了类方法和实例方法来创建NSDictionary,两种创建方式需要传入的参数基本相似,只是类方法以dictionary开头,而实例方法则以init开头。

下面是创建NSDictionary对象的几类常见的方法。


dictionary:创建一个不包含任何key-value对的NSDictionary。
dictionary WithContentsOfFile:/initWithContentsOfFile::读取指定文件的内容,使用指定的文件内容来初始化NSDictionary。该文件通常是由NSDictionary输出生成的。
dictionaryWithDictionary:/initWithDictionary::使用已有的NSDictionary包含的key-value对来初始化NSDictionary对象。
dictionaryWithObject:forKey::使用单个key-value对来创建NSDictionary对象。dictionary WithObjects:forKeys:/initWithObjects:forKeys::使用两个 NSArray分别指定key、value集合,可以创建包含多个key-value对的NSDictionary。
dictionaryWithObjectsAndKeys:/initWithObjectsAndKeys::调用该方法时,需要按valuel,keyl,value2,key2,…nil的格式传入多个key-value对。


除此之外,还可使用如下简化语法来创建NSDictionary对象:
@(keyl: valuel,key2:value2.....)
一旦得到NSDictionary对象,接下来就可以通过方法来访问该集合所包含的key或value。NSDictionary提供了如下常用的方法。

count:该方法返回该NSDictionary所包含的 key-value对的数量。
allKeys:该方法返回该NSDictionary所包含的全部 key。
allKeysForObject::该方法返回指定value对应的全部 key。
allValues:该方法返回该NSDictionary所包含的全部 value。
objectForKey::该方法获取该NSDictionary中指定key对应的value。
objectForKeyedSubscript:通过该方法的支持,允许NSDictionary通过下标法来获取指
定key对应的value。
valueForKey::该方法获取该NSDictionary中指定kev对应的 value。
keyEnumerator:该方法返回用于遍历该NSDictionary所有key的NSEnumerator对象。objectEnumerator:该方法返回用于遍历该NSDictionary所有value的 NSEnumerator对象。
enumerateKeysAndObjectsUsingBlock::使用指定的代码块来迭代执行该集合中所有的key-value对。
enumerateKeysAndObjectsWithOptions:usingBlock::使用指定的代码块来迭代执行该集合中所有的key-value对。该方法可以传入一个额外的NSEnumerationOptions参数。

writeToFile:atomically::将该NSDictionary对象的数据写入指定文件。

main.m

#import <Foundation/Foundation.h>
#import "FKUser.h"
#import "NSDictionary+print.h"
int main(int argc, const char * argv[]) {
    @autoreleasepool {
        NSDictionary* dict = [NSDictionary dictionaryWithObjectsAndKeys:
                              [[FKUser alloc] initWithName:@"sun" pass:@"123"], @"one",
                              [[FKUser alloc] initWithName:@"bai" pass:@"345"], @"two",
                              [[FKUser alloc] initWithName:@"zhu" pass:@"654"], @"three",
                              [[FKUser alloc] initWithName:@"tang" pass:@"178"],@"four",
                              [[FKUser alloc] initWithName:@"niu" pass:@"155"], @"five", nil];
        [dict print];
        NSLog(@"dictb包含%ld个key-value对", [dict count]);
        NSLog(@"dict的所有key为%@", [dict allKeys]);
        NSLog(@"<FKUser[name = sun, pass = 123]>对应的所有key为%@", [dict allKeysForObject:[[FKUser alloc] initWithName:@"sun" pass:@"123"]]);
        //获取遍历dict所有Value的枚举器
        NSEnumerator* en = [dict objectEnumerator];
        NSObject* Value;
        //用枚举器来遍历dict中所有的value
        while (Value = [en nextObject]) {
            NSLog(@"%@", Value);
        }
        [dict enumerateKeysAndObjectsUsingBlock:^(id key, id value, BOOL* stop) {
            NSLog(@"key的值为:%@", key);
            [value say:@"123"];
        }];
    }
    return 0;
}

NSDictionary+print.h

#import <Foundation/Foundation.h>

NS_ASSUME_NONNULL_BEGIN

@interface NSDictionary (print)
- (void) print;
@end

NS_ASSUME_NONNULL_END

NSDictionary+print.m

#import "NSDictionary+print.h"

@implementation NSDictionary (print)
- (void) print {
    NSMutableString* result = [NSMutableString stringWithString:@"{"];
    for (id key in self) {
        [result appendString:[key description]];
        [result appendString:@"="];
        [result appendString:[self[key] description]];
        [result appendString:@", "];
    }
    NSUInteger len = [result length];
    [result deleteCharactersInRange:NSMakeRange(len - 2, 2)];
    [result appendString:@"}"];
    NSLog(@"%@", result);
}
@end

通过key来获取value有两种方法:调用NSDictionary的objectForKey:方法

直接使用下标法

[dictionary objectForKey:key];

dictionary[key];

FKUser.h

#import <Foundation/Foundation.h>

NS_ASSUME_NONNULL_BEGIN

@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

NS_ASSUME_NONNULL_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相等,即可认为他们相等
- (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;
}
//重写description方法,可以直接看到FKUser对象的状态
- (NSString*) description {
    return [NSString stringWithFormat:@"<FKUser[name = %@, pass = %@]>", self.name, self.pass];
}
@end

对 NSDictionary的key排序


NSDictionary还提供了方法对NSDictionay的所有key执行排序,这些方法执行完成后将返回排序好的所有key组成的NSArray

NSDictionary提供的排序方法如下。
keysSortedByValueUsingSelector::根据NSDictionary的所有value的指定方法的返回值对key排序;调用value的该方法必须返回NSOrderedAscending、NSOrderedDescending、NSOrderedSame这三个枚举值之一。
keysSortedByValueUsingComparator::该方法使用指定的代码块来遍历key-value对,并根据执行结果(执行结果必须返回NSOrderedAscending、NSOrderedDescending、NSOrderedSame这三个枚举值之一)对NSDictionary的所有key进行排序。
keysSortedBy ValueWithOptions:usingComparator:与前一个方法的功能相似,只是该方法可以传入一个额外的NSEnumerationOptions参数。 

#import <Foundation/Foundation.h>
#import "NSDictionary+print.h"
int main(int argc, const char * argv[]) {
    @autoreleasepool {
        NSDictionary* dict = [NSDictionary dictionaryWithObjectsAndKeys:@"123", @"one", @"1234", @"tow", @"12", @"three", @"12345", @"four", nil];
        [dict print];
        NSArray* keyarr1 = [dict keysSortedByValueUsingSelector:@selector(compare:)];
        NSLog(@"%@", keyarr1);
        NSArray* keyarr2 = [dict keysSortedByValueUsingComparator:^(id value1, id value2) {
            if ([value1 length] > [value2 length]) {
                return NSOrderedDescending;
            }
            if ([value1 length] < [value2 length]) {
                return NSOrderedAscending;
            }
            return NSOrderedSame;
        }];
        NSLog(@"%@", keyarr2);
        [dict writeToFile:@"mydict.txt" atomically:YES];
    }
    return 0;
}

NSDictionary+print.h和NSDictionary+print.m和上面代码相同。

对 NSDictionary的key进行过滤


NSDictionary还提供了方法对NSDictionary的所有key执行过滤,这些方法执行完成后将返回满足过滤条件的key组成的 NSSet

NSDictionary提供了如下过滤方法。

keysOfEntriesPassingTest::使用代码块迭代处理 NSDictionary的每个key-value对。对NSDictionary的key-value对进行过滤,该代码块必须返回BOOL类型的值,只有当该代码块返回YES时,该key才会被保留下来;该代码块可以接受3个参数,其中第一个参数代表正在迭代处理的key,第二个参数代表正在迭代处理的value,第三个参数代表是否还需要继续迭代,如果将第三个参数设为NO,那么该迭代会立即停止。

keysOfEntriesWithOptions:passingTest::该方法的功能与前一个方法的功能基本相同。只是该方法可以额外传入一个附加的NSEnumerationOptions选项参数。 

#import <Foundation/Foundation.h>

int main(int argc, const char * argv[]) {
    @autoreleasepool {
        NSDictionary* dict = [NSDictionary dictionaryWithObjectsAndKeys:[NSNumber numberWithInt:89], @"12",[NSNumber numberWithInt:69], @"128", [NSNumber numberWithInt:100], @"1324", [NSNumber numberWithInt:1], @"1223",  nil];
        NSSet* keyset = [dict keysOfEntriesPassingTest:^(id key, id value, BOOL* stop) {
            return (BOOl)([value intValue] > 80);
        }];
        NSLog(@"%@", keyset);
    }
    return 0;
}

使用自定义类作为NSDictionary的key 

前面介绍NSDictionary的功能和用法时,都是使用NSString作为NSDictionary的key。如果程序打算使用自定义类作为NSDictionary的key,则该自定义类必须满足如下要求。

该自定义类正确重写过 isEqual:和hash方法。所谓正确重写,是指当两个对象通过isEqual判断相等时,两个对象的hash方法返回值也相等。

义类必须实现了copyWithZone:方法,该方法最好返回该对象的不可变副本。

从这个意义上看,key相当于NSDictionary元素的索引。如果key本身是可变的,且程序可以通过其他变量来修改NSDictionary的key,这就可能导致NSDictionary的“索引”值被破坏,从而导致NSDictionary的完整性被破坏。
为了避免上面所描述的情况出现,NSDictionary采用了更安全的做法,只要程序把任何对象都作为key放入 NSDictionary中,NSDictionary总会先调用该key的copy方法来复制该对象的不可变副本,然后使用该副本作为NSDictionary的key。


为了让前面的FKUser 类可作为NSDictionary的 key,还需要让该 FKUser 类实现NSCopying协议(这是可选的,通常建议实现该协议),并让该FKUser 类实现 copyWithZone:方法。FKUser实现的copyWithZone:方法如下 

- (id)copyWithZone:(NSZone*) zone {
    NSLog(@"---正在复制---");
    FKUser* newUser = [[[self class] allocWithZone:zone] init];
    newUser->name = name;
    newUser->pass = pass;
    return newUser;
}

 "FKUser.h"  "NSDictionary+print.h"代码与前面相同

#import <Foundation/Foundation.h>
#import "FKUser.h"
#import "NSDictionary+print.h"
int main(int argc, const char * argv[]) {
    @autoreleasepool {
        FKUser* u1 = [[FKUser alloc] initWithName:@"bai" pass:@"345"];
        NSDictionary* dict = [NSDictionary dictionaryWithObjectsAndKeys:
                             @"one",  [[FKUser alloc] initWithName:@"sun" pass:@"123"],
                             @"two", u1,
                             @"three",  [[FKUser alloc] initWithName:@"sun" pass:@"123"],
                             @"four" , [[FKUser alloc] initWithName:@"tang" pass:@"178"],
                             @"five" , [[FKUser alloc] initWithName:@"niu" pass:@"155"], nil];
        u1.pass = nil;
        [dict print];
    }
    return 0;
}

 

程序依次调用了4次copyWithZone:方法来复制FKUser的副本,这是因为该NSDictionary中只包含了4个key-value对。虽然程序指定了5个key-value对,但其中有两个key(FKUser对象)的name、pass完全相同,这两个对象通过isEqual:方法比较会返回YES,并且它们的hash方法返回值也相等,因此NSDictionary会认为这两个 key是重复的,就会只保留一个。


 NSMutableDictionary的功能与用法


NSMutableDictionary继承了NSDictionary,它代表一个key-value对可变的NSDictionary集合。

NSMutableDictionary主要新增了如下方法

setObject:forKey:设置一个key-value对。如果NSDictionary中没有包含与该key相同的key-value对,那么NSDictionary将会新增一个key-value对;否则该key-value对将会覆盖已有的key-value对。
setObject:forKeyedSubscript::通过该方法的支持,允许程序通过下标法来设置key-value对。
addEntriesFromDictionary:将另一个 NSDictionary中所有的 key-value 对复制到当前NSDictionary中。
setDictionary:用另一个NSDictionary中所有的key-value对替换当前NSDictionary中的key-value对。
removeObjectForKey:根据key来删除key-value对。
removeAllObjects:清空该NSDictionary。
removeObjectsForKeys:使用多个 key组成的NSArray作为参数,同时删除多个key对应的key-value对。 

#import <Foundation/Foundation.h>
#import "NSDictionary+print.h"
int main(int argc, const char * argv[]) {
    @autoreleasepool {
        NSMutableDictionary* dict = [NSMutableDictionary dictionaryWithObjectsAndKeys:[NSNumber numberWithInt:89], @"疯狂Android", nil];
        //通过下标设置key-value对,如果已存在key,则覆盖原来的value
        //如果不充在,则添加成功
        dict[@"疯狂Android"] = [NSNumber numberWithInt:99];
        [dict print];
        dict[@"疯狂MXL讲义"] = [NSNumber numberWithInt:88];
        [dict print];
        NSDictionary* dict2 = [NSDictionary dictionaryWithObjectsAndKeys:[NSNumber numberWithInt:11], @"123", [NSNumber numberWithInt:22], @"234",  nil];
        [dict addEntriesFromDictionary:dict2];
        [dict print];
        [dict removeObjectForKey:@"234"];
        [dict print];
    }
    return 0;
}

上面程序对NSMutableDictionary中key-value对的增加,删除操作。 

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值