【问题标题】:Sorting `NSArray` based on C array values根据 C 数组值对 `NSArray` 进行排序
【发布时间】:2013-10-15 09:13:59
【问题描述】:

我有一个带有对象的NSArray 和一个带有双精度的 C 数组。我想根据 C 数组值对 NSArray 对象进行排序。

我能想到的唯一方法如下(基于this SO question):

NSArray *sortedArray;
sortedArray = [origArray sortedArrayUsingComparator:^NSComparisonResult(id a, id b) 
{
    // Getting c array values at the same indices as the objects being compared
    NSNumber *aValue = @(cArray[[origArray indexOfObject:a]]);
    NSNumber *bValue = @(cArray[[origArray indexOfObject:b]]);
    // Returning comparison result
    return [aValue compare:bValue];
}];

但我认为indexOfObject: 部分有点贵。是这样吗?

如果是这样,有没有更快的方法?

编辑:

为了提供更多上下文,此排序是优化算法适应度评估的一部分。在我的程序中,我做了一个基本的分析,健身评估代码变成了瓶颈。因此,我正在尝试对其进行优化。

此外,数组中的元素数量预计在 100 到 10k 之间。

【问题讨论】:

    标签: objective-c arrays performance sorting


    【解决方案1】:

    在没有真正的性能问题的情况下推测潜在的性能问题是没有意义的。

    如果您可以测量代码的实际问题(例如,通过在 Instruments 中使用 Time Profiler),您就会知道瓶颈可能在哪里。在您的代码中,有几个地方可能会减慢排序速度(将每个数字装箱以计算 NSComparisonResult,保留/释放来自 ARC 的开销仅用于访问数组中的对象,...)。

    有几种方法可以提高性能,但它们取决于实际数据(origArray 中的对象数量、C 数组双精度数的来源……)。

    如果我猜测一下似乎可以提高性能的候选者,我会说应该消除有两个数组的一般问题,其中元素形成一个元组而不是一个元组数组。这是一个 C 函数,它以很小的开销对数组进行排序:

    NSArray *sortedObjects(double valuesArray[], NSArray *objects)
    {
        NSUInteger count = [objects count];
        struct tuple {
            double value;
            __unsafe_unretained id object;
        } tupleArray[count];
    
        for (NSUInteger i = 0; i < count; ++i)
            tupleArray[i] = (struct tuple){ valuesArray[i], objects[i] };
    
        qsort_b(tupleArray, count, sizeof tupleArray[0], ^int(const void *a, const void *b) {
            double v = ((const struct tuple *)a)->value - ((const struct tuple *)b)->value;
            return v == 0 ? 0 : v > 0 ? 1 : -1;
        });
    
        __unsafe_unretained id objectsArray[count];
        for (NSUInteger i = 0; i < count; ++i)
            objectsArray[i] = tupleArray[i].object;
    
        return [[NSArray alloc] initWithObjects:objectsArray count:count];
    }
    

    【讨论】:

    • 感谢您的回答和清晰优雅的示例。我在我的问题中添加了一些信息以供澄清。如果我理解正确,这里的一般方法是以某种方式创建对象排序值对的元组(通过结构,或通过关联对象或常规对象,正如@JeremyP 建议的那样)。
    【解决方案2】:

    在您知道这是一个问题之前不要优化排序。首先你需要回答一些问题。你的数组中有多少个值?预计用户可以等待多长时间?

    之后,您应该测试当前的解决方案。对预期数量的值进行排序时,用户是否需要等待太长时间?

    接下来,回答其他架构问题。例如,您可以在用户做其他工作时在后台对数组进行排序吗?

    如果你做了所有这些,你发现了一个问题,那么你就可以开始考虑如何优化了。

    首先想到的是将数组映射到字典中,对字典进行排序,然后映射回排序后的数组。

    首先,获取一个映射类别(这是我在NSArray Equivalent of Map 找到的)。

    @interface NSArray (Map)
    - (NSArray *)mapObjectsUsingBlock:(id (^)(id obj, NSUInteger idx))block;
    @end
    
    @implementation NSArray (Map)
    - (NSArray *)mapObjectsUsingBlock:(id (^)(id obj, NSUInteger idx))block {
        NSMutableArray *result = [NSMutableArray arrayWithCapacity:[self count]];
        [self enumerateObjectsUsingBlock:^(id obj, NSUInteger idx, BOOL *stop) {
            [result addObject:block(obj, idx)];
        }];
        return result;
    }
    @end
    

    一旦您选择了映射方法,然后运行新的排序。

    // Step 1) Map into an array of a dictionaries
    NSArray *dicts = [origArray mapObjectsUsingBlock:^(id obj, NSUInteger idx) {
        return @{ @"value": obj, @"sortValue": @(cArray[idx]) };
    }];
    
    // Step 2) Sort the dictionaries
    NSArray *sortedDicts = [dicts sortedArrayUsingComparator:^NSComparisonResult(id a, id b) 
    {
        return [a[@"sortValue"] compare:b[@"sortValue"]];
    }];
    
    // Step 3) Map back to the sorted array
    sortedArray = [sortedDicts mapObjectsUsingBlock:^(id obj, NSUInteger idx) {
        return obj[@"value"];
    }];
    

    所有这些之后,您需要重复测试以回答“用户在对预期数量的值进行排序时是否需要等待太久?”的问题?如果答案仍然是肯定的,用户必须等待太久,那么您需要再次寻找新的解决方案。

    【讨论】:

    • 感谢您的回答和有趣的方法。
    【解决方案3】:

    如果是这样,有没有更快的方法?

    那么显而易见的问题是为什么你的对象没有双值属性?

    如果你绝对不能有一个双值的属性(实际上,你总是可以使用associated objects),你可以创建一个包装器对象,其中一个属性是原始对象,一个属性是双倍值并对这些数组进行排序。但是,这非常昂贵(大量分配),因此您肯定要先进行其他两个答案建议的分析。

    【讨论】:

    • 感谢您对关联对象的引用,我不知道。关于答案的第二部分,我想避免创建包装器对象,因为每次都需要使用不同的排序值对不同的对象进行排序(因此无法缓存包装器)。因此,我认为(正如您所指出的)这种方法会有很大的开销。
    • 另一方面,我相信它仍然比我在问题中提出的方法更好。
    【解决方案4】:

    如果您唯一关心的是indexOfObject: 的开销,您可能希望使用indexOfObjectIdenticalTo:。它为每个元素省略了isEqual: 消息,如果数组有很多元素,可能会更快。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2013-05-14
      • 2015-04-08
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多