从NSArray
比较包含NSNumbers
的{{1}}的所有值来找到最大的和最小的一个,有什么有用而且很棒的方法?
如何在Objective-C中如何做到这一点好又快?
答案 0 :(得分:141)
如果执行速度(不是编程速度)很重要,那么显式循环是最快的。我用1000000个随机数的数组进行了以下测试:
版本1:对数组进行排序:
NSArray *sorted1 = [numbers sortedArrayUsingSelector:@selector(compare:)];
// 1.585 seconds
版本2:键值编码,使用“doubleValue”:
NSNumber *max=[numbers valueForKeyPath:@"@max.doubleValue"];
NSNumber *min=[numbers valueForKeyPath:@"@min.doubleValue"];
// 0.778 seconds
第3版:键值编码,使用“self”:
NSNumber *max=[numbers valueForKeyPath:@"@max.self"];
NSNumber *min=[numbers valueForKeyPath:@"@min.self"];
// 0.390 seconds
版本4:显式循环:
float xmax = -MAXFLOAT;
float xmin = MAXFLOAT;
for (NSNumber *num in numbers) {
float x = num.floatValue;
if (x < xmin) xmin = x;
if (x > xmax) xmax = x;
}
// 0.019 seconds
第5版:阻止枚举:
__block float xmax = -MAXFLOAT;
__block float xmin = MAXFLOAT;
[numbers enumerateObjectsUsingBlock:^(NSNumber *num, NSUInteger idx, BOOL *stop) {
float x = num.floatValue;
if (x < xmin) xmin = x;
if (x > xmax) xmax = x;
}];
// 0.024 seconds
测试程序创建一个包含1000000个随机数的数组,然后应用所有排序 同一阵列的技术。上面的时间是一次运行的输出,但我在每次运行中进行了大约20次运行,结果非常相似。我还改变了应用5种排序方法的顺序,以排除缓存效果。
更新:我现在已经创建了一个(希望)更好的测试程序。完整的源代码在这里:https://gist.github.com/anonymous/5356982。排序的平均时间 1000000随机数的数组(以秒为单位,在3.1 GHz Core i5 iMac上,发布编译):
Sorting 1.404 KVO1 1.087 KVO2 0.367 Fast enum 0.017 Block enum 0.021
更新2:正如我们所看到的,快速枚举比块枚举更快(这里也说明了http://blog.bignerdranch.com/2337-incremental-arrayification/)。
编辑:以下完全错误,因为我忘记初始化用作锁定的对象,因为Hot Licks正确注意到,因此根本没有完成同步。
使用lock = [[NSObject alloc] init];
并发枚举这么慢
我不敢透露结果。也许更快的同步机制可能
帮助...)
<击>
如果您将NSEnumerationConcurrent
选项添加到,则会发生显着变化
阻止枚举:
__block float xmax = -MAXFLOAT;
__block float xmin = MAXFLOAT;
id lock;
[numbers enumerateObjectsWithOptions:NSEnumerationConcurrent usingBlock:^(NSNumber *num, NSUInteger idx, BOOL *stop) {
float x = num.floatValue;
@synchronized(lock) {
if (x < xmin) xmin = x;
if (x > xmax) xmax = x;
}
}];
这里的时间是
Concurrent enum 0.009
所以它的速度大约是快速枚举的两倍。结果可能不具代表性
因为它取决于可用的线程数。但无论如何有趣!请注意我
使用了“最容易使用”的同步方法,这种方法可能不是最快的。
击>
答案 1 :(得分:14)
通过在NSNumber下包装然后
来保存浮动NSNumber *max=[numberArray valueForKeyPath:@"@max.doubleValue"];
NSNumber *min=[numberArray valueForKeyPath:@"@min.doubleValue"];
* 未编译和检查,已经使用intValue检查,不确定是double还是float
答案 2 :(得分:2)
排序。采取第一个和最后一个元素。
btw:你不能在NSArray中存储浮动,你需要将它们包装在NSNumber对象中。
NSArray *numbers = @[@2.1, @8.1, @5.0, @.3];
numbers = [numbers sortedArrayUsingSelector:@selector(compare:)];
float min = [numbers[0] floatValue];
float max = [[numbers lastObject] floatValue];
答案 3 :(得分:0)
我同意对数组进行排序然后选择第一个和最后一个元素,但我发现这个解决方案更优雅(通过更改块内的比较,这也适用于非数字对象):
NSArray *unsortedArray = @[@(3), @(5), @(1)];
NSArray *sortedArray = [unsortedArray sortedArrayUsingComparator:^NSComparisonResult(id obj1, id obj2) {
NSNumber *item1 = (NSNumber *)obj1;
NSNumber *item2 = (NSNumber *)obj2;
return [item1 compare:item2];
}];
如果你真的想得到想象并有一个非常长的列表并且你不想阻止你的主线程,这应该有效:
NSComparator comparison = ^NSComparisonResult(id obj1, id obj2) {
NSNumber *item1 = (NSNumber *)obj1;
NSNumber *item2 = (NSNumber *)obj2;
return [item1 compare:item2];
};
void(^asychSort)(void) = ^
{
NSArray *sortedArray = [unsortedArray sortedArrayUsingComparator:comparison];
dispatch_sync(dispatch_get_main_queue(), ^{
NSLog(@"Finished Sorting");
//do your callback here
});
};
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), asychSort);
答案 4 :(得分:0)
简单
NSArray *numbers = @[@2.1, @8.1, @5.0, @.3];
numbers = [numbers sortedArrayUsingSelector:@selector(compare:)];
float min = [numbers[0] floatValue];
float max = [[numbers lastObject] floatValue];
NSLog(@"MIN%f",min);
NSLog(@"MAX%f",max);