数字/字符排序及数据遍历快慢探究
enumerateObjectsUsingBlock、ForLoop、ForIn、EnumerateObjectsWithOptions PK
0. viewDidLoad中调用函数
- (void)viewDidLoad { [super viewDidLoad]; //数字排序 [self numberSort]; //字符排序 [self charSort]; //遍历数组快慢 enumerateObjectsUsingBlock ForLoop ForIn [self ForLoopAndForInAndEnumerateObjectsUsingBlock]; //通过Value查找Index快慢 ForIn EnumerateObjectsUsingBlock EnumerateObjectsWithOptions [self ForInAndEnumerateObjectsUsingBlockAndEnumerateObjectsWithOptions]; //遍历字典快慢 ForIn enumerateKeysAndObjectsUsingBlock [self ForInAndEnumerateKeysAndObjectsUsingBlock]; //数组的遍历 enumerateObjectsUsingBlock enumerateObjectsAtIndexes [self enumArrayWithEnumBlockAndEnumIndexes]; }
1. 数字、字符比较排序
#pragma mark 比较/排序 #pragma mark - 字符排序 - - (void)charSort { UILabel *lable = [[UILabel alloc] initWithFrame:CGRectMake(10, 120, kDeviceWidth - 20, 50)]; lable.backgroundColor = [UIColor greenColor]; lable.numberOfLines = 0; [self.view addSubview:lable]; NSArray *charArray = @[@"home",@"data",@"date",@"hello",@"key",@"jack"]; /* 对于NSStringCompareOptions,看看系统的说明: enum{ NSCaseInsensitiveSearch = 1,//不区分大小写比较 NSLiteralSearch = 2,//区分大小写比较 NSBackwardsSearch = 4,//从字符串末尾开始搜索 NSAnchoredSearch = 8,//搜索限制范围的字符串 NSNumbericSearch = 64//按照字符串里的数字为依据,算出顺序。例如 Foo2.txt < Foo7.txt < Foo25.txt //以下定义高于 mac os 10.5 或者高于 iphone 2.0 可用 , NSDiacriticInsensitiveSearch = 128,//忽略 "-" 符号的比较 NSWidthInsensitiveSearch = 256,//忽略字符串的长度,比较出结果 NSForcedOrderingSearch = 512//忽略不区分大小写比较的选项,并强制返回 NSOrderedAscending 或者 NSOrderedDescending //以下定义高于 iphone 3.2 可用 , NSRegularExpressionSearch = 1024//只能应用于 rangeOfString:..., stringByReplacingOccurrencesOfString:...和 replaceOccurrencesOfString:... 方法。使用通用兼容的比较方法,如果设置此项,可以去掉 NSCaseInsensitiveSearch 和 NSAnchoredSearch } */ NSStringCompareOptions comparisonOptions = NSCaseInsensitiveSearch|NSNumericSearch| NSWidthInsensitiveSearch|NSForcedOrderingSearch; NSComparator sort = ^(NSString *obj1,NSString *obj2){ NSRange range = NSMakeRange(0,obj1.length); return [obj1 compare:obj2 options:comparisonOptions range:range]; }; NSArray *resultArray2 = [charArray sortedArrayUsingComparator:sort]; // NSLog(@"字符串数组排序结果%@",resultArray2); NSMutableString *muStr = [NSMutableString string]; [muStr appendString:@"(2) 字符串数组排序结果:\n"]; for (NSString *str in resultArray2) { [muStr appendFormat:@"%@ -",str]; } lable.text = muStr; } #pragma mark - 数字排序 - - (void)numberSort { UILabel *lable = [[UILabel alloc] initWithFrame:CGRectMake(10, 50, kDeviceWidth - 20, 50)]; lable.backgroundColor = [UIColor greenColor]; lable.numberOfLines = 0; [self.view addSubview:lable]; NSArray *originalArray = @[@"1",@"21",@"12",@"11",@"0"]; //block比较方法,数组中可以是NSInteger,NSString(需要转换) NSComparator finderSort = ^(id string1,id string2){ if ([string1 integerValue] > [string2 integerValue]) { return (NSComparisonResult)NSOrderedDescending; }else if ([string1 integerValue] < [string2 integerValue]){ return (NSComparisonResult)NSOrderedAscending; } else return (NSComparisonResult)NSOrderedSame; }; //数组排序: NSArray *resultArray = [originalArray sortedArrayUsingComparator:finderSort]; // NSLog(@"第一种排序结果:%@",resultArray); NSMutableString *muStr = [NSMutableString string]; [muStr appendString:@"(1) 数字排序结果:\n"]; for (NSString *str in resultArray) { [muStr appendFormat:@"%@ -",str]; } lable.text = muStr; }
2. 遍历快慢
#pragma mark - 遍历字典快慢 - - (void)ForInAndEnumerateKeysAndObjectsUsingBlock { NSMutableDictionary *testDictionary = [NSMutableDictionary dictionary]; for (int i = 0; i < 1000; i++) { NSString *valueStr = [NSString stringWithFormat:@"jack%04d",i]; NSString *keyStr = [NSString stringWithFormat:@"name%04d",i]; [testDictionary setValue:valueStr forKey:keyStr]; } // NSLog(@"========== %@",testDictionary); //For - in NSMutableArray *forInArry = [NSMutableArray array]; double date_s = CFAbsoluteTimeGetCurrent(); NSArray *keys = [testDictionary allKeys]; for (NSString *key in keys) { NSString *Value = testDictionary[key]; [forInArry addObject:Value]; } double date_current = CFAbsoluteTimeGetCurrent() - date_s; NSLog(@" For-in Time: %f ms",date_current * 1000); //enumerateKeysAndObjectsUsingBlock date_s = CFAbsoluteTimeGetCurrent(); NSMutableArray *enumArry = [NSMutableArray array]; [testDictionary enumerateKeysAndObjectsUsingBlock:^(id key, id obj, BOOL *stop) { [enumArry addObject:obj]; }]; date_current = CFAbsoluteTimeGetCurrent() - date_s; NSLog(@" enumerateBlock Time: %f ms",date_current * 1000); //enumerateKeysAndObjectsWithOptions date_s = CFAbsoluteTimeGetCurrent(); NSMutableArray * enumOptionArr = [NSMutableArray array]; [testDictionary enumerateKeysAndObjectsWithOptions:NSEnumerationConcurrent usingBlock:^(id _Nonnull key, NSString *obj, BOOL * _Nonnull stop) { [enumOptionArr addObject:obj]; }]; date_current = CFAbsoluteTimeGetCurrent() - date_s; NSLog(@"enumerateOptions Time: %f ms",date_current * 1000); // NSLog(@"ForInArr: %@",forInArry); // NSLog(@"enumArry: %@",enumArry); // NSLog(@"enumOptionArr: %@",enumOptionArr); /* 输出结果: For-in Time: 0.099003 ms enumerateBlock Time: 0.070989 ms enumerateOptions Time: 0.105023 ms enumerateBlock更快 结论: 当我们想遍历字典类型的时候, 推荐使用enumerateBlock(经测试:大量数据enumerateBlock更快,若数据量较小For-in更快) */ } #pragma mark - 通过Value查找Index快慢 - - (void)ForInAndEnumerateObjectsUsingBlockAndEnumerateObjectsWithOptions { NSMutableArray *test = [NSMutableArray array]; for (int i = 0; i < 10000000; i ++) { [test addObject:@(i + 10)]; } //For-in __block NSInteger index = 0; double date_s = CFAbsoluteTimeGetCurrent(); for (NSNumber *num in test) { if ([num integerValue] == 9999999) { index = [test indexOfObject:num]; break; } } double date_current = CFAbsoluteTimeGetCurrent() - date_s; NSLog(@"index : %ld For-in Time: %f ms",(long)index,date_current * 1000); //enumerateObjectsUsingBlock index = 0; date_s = CFAbsoluteTimeGetCurrent(); [test enumerateObjectsUsingBlock:^(id num, NSUInteger idx, BOOL *stop) { if ([num integerValue] == 9999999) { index = idx; *stop = YES; } }]; date_current = CFAbsoluteTimeGetCurrent() - date_s; NSLog(@"index : %ld enumerateBlock Time: %f ms",(long)index,date_current * 1000); //enumerateObjectsWithOptions index = 0; date_s = CFAbsoluteTimeGetCurrent(); [test enumerateObjectsWithOptions:NSEnumerationConcurrent usingBlock:^(id num, NSUInteger idx, BOOL *stop) { if ([num integerValue] == 9999999) { index = idx; *stop = YES; } }]; date_current = CFAbsoluteTimeGetCurrent() - date_s; NSLog(@"index : %ld enumerateObjectsWithOptions Time: %f ms",(long)index,date_current * 1000); /* 输出结果: index : 9999989 For-in Time: 572.869003 ms index : 9999989 enumerateBlock Time: 439.854980 ms index : 9999989 enumerateObjectsWithOptions Time: 279.659986 ms enumerateObjectsWithOptions方法最快 结论: 通过Value查询index的时候, 面对大量的数组推荐使用 enumerateObjectsWithOptions的并行方法. For-in和enumerateObjectsWithOptions方法这里我比较喜欢第二种写法简洁直观. */ } #pragma mark - 遍历数组快慢 - - (void)ForLoopAndForInAndEnumerateObjectsUsingBlock { NSMutableArray *muArrM = [NSMutableArray array]; for (int i = 0; i < 1000000; i++) { [muArrM addObject:@(i)]; } __block int sum = 0; double date_s = CFAbsoluteTimeGetCurrent(); for (int i = 0; i < muArrM.count; i++) { sum += [muArrM[i] integerValue]; } double date_current = CFAbsoluteTimeGetCurrent() - date_s; NSLog(@"Sum : %d forLoop Time : %f ms",sum, date_current * 1000); sum = 0; date_s = CFAbsoluteTimeGetCurrent(); for (NSNumber *num in muArrM) { sum += [num integerValue]; } date_current = CFAbsoluteTimeGetCurrent() - date_s; NSLog(@"Sum : %d ForIn Time: %f ms",sum, date_current * 1000); sum = 0; date_s = CFAbsoluteTimeGetCurrent(); [muArrM enumerateObjectsUsingBlock:^(id _Nonnull obj, NSUInteger idx, BOOL * _Nonnull stop) { sum += [obj integerValue]; }]; date_current = CFAbsoluteTimeGetCurrent() - date_s; NSLog(@"Sum: %d enumrateBlock Time:%f ms", sum, date_current * 1000); /* 输出结果: Sum : 1783293664 forLoop Time : 30.366004 ms Sum : 1783293664 ForIn Time: 13.710976 ms Sum: 1783293664 enumrateBlock Time:44.068038 ms For-in方法最快速 结论: 当只是遍历一个数组的时候使用ForIn会比较快速, 推荐使用For-in遍历数组. */ }
3. 数组的遍历
#pragma mark - enumArrayWithEnumBlockAndEnumIndexes - - (void)enumArrayWithEnumBlockAndEnumIndexes { //对数组中元素块操作 NSArray* array = @[@"aa",@"bb",@"cc",@"dd",@"ee",@"ff",@"gg"]; [array enumerateObjectsUsingBlock:^(id obj, NSUInteger idx, BOOL *stop) { NSLog(@"%@ - %lu",obj,idx); }]; NSLog(@"-----enumerateObjectsUsingBlock--------- "); //NSIndexSex 是取值范围 (NSMakeRange(1, 4)前面数字代表从哪个位置开始取,后面数字代表取的长度) //options为NSEnumerationReverse倒序,NSEnumerationConcurrent正序 [array enumerateObjectsAtIndexes:[NSIndexSet indexSetWithIndexesInRange:NSMakeRange(1, 4)] options:NSEnumerationConcurrent usingBlock:^(id obj, NSUInteger idx, BOOL *stop) { NSLog(@"%@ - %lu",obj, idx); }]; NSLog(@"-----enumerateObjectsAtIndexes--------- "); }
enumerateObjectsUsingBlock VS for(... in ...)
for(... in ...)用起来非常方便、简洁,同时
enumerateObjectsUsingBlock:
也有很多新特性:
-
通常enumerateObjectsUsingBlock:
和 (for(... in ...)在效率上基本一致,有时会快些。主要是因为它们都是基于
NSFastEnumeration
实现的. 快速迭代在处理的过程中需要多一次转换,当然也会消耗掉一些时间. 基于Block的迭代可以达到本机存储一样快的遍历集合. 对于字典同样适用,而数组的迭代却不行。 -
注意"enumerateObjectsUsingBlock" 修改局部变量时, 你需要声明局部变量为
__block 类型
. -
enumerateObjectsWithOptions:usingBlock:
支持并发迭代或反向迭代,并发迭代时效率也非常高. -
对于字典而言,
enumerateObjectsWithOptions:usingBlock
也是唯一的方式可以并发实现恢复Key-Value值.