【问题标题】:Fastest way to check if an array contains the same objects of another array检查一个数组是否包含另一个数组的相同对象的最快方法
【发布时间】:2013-02-02 19:41:36
【问题描述】:

目标是比较两个数组并检查它们是否包含相同的对象(尽可能快 - 数组中有很多对象)。无法使用 isEqual: 检查数组,因为它们的排序方式不同。

我已经尝试过此处发布的解决方案(https://stackoverflow.com/a/1138417 - 请参阅 Peter Hosey 帖子的最后一个代码 sn-p)。但这不适用于不同排序的数组。

我现在使用的代码如下:

+ (BOOL)arraysContainSameObjects:(NSArray *)array1 andOtherArray:(NSArray *)array2 {
    // quit if array count is different
    if ([array1 count] != [array2 count]) return NO;

    BOOL bothArraysContainTheSameObjects = YES;
    for (id objectInArray1 in array1) {
        BOOL objectFoundInArray2 = NO;
        for (id objectInArray2 in array2) {
            if ([objectInArray1 isEqual:objectInArray2]) {
                objectFoundInArray2 = YES;
                break;
            }
        }
        if (!objectFoundInArray2) {
            bothArraysContainTheSameObjects = NO;
            break;
        }
    }

    return bothArraysContainTheSameObjects;
}

这可行,但这是两个嵌套的快速枚举。有没有办法进行更快的比较?

【问题讨论】:

  • 您是否还必须验证它具有相同数量的匹配对象?例如,如果数组 1 有 2 个 X 实例,但数组 2 只有 1 则它会失败?
  • 是的,这也应该验证。
  • 在这种情况下,您别无选择,只能对它们进行排序并进行比较。
  • 这有帮助吗:cocoabuilder.com/archive/cocoa/… - - (unsigned)numberOfDistinctObjects { return [[NSSet setWithArray:self] count]; }
  • 嗯,这个比较是O(n),加上排序算法的效率。

标签: ios objective-c nsarray fast-enumeration


【解决方案1】:

根据您的代码,您对相同数量的元素是严格的,并且第一个数组的每个对象都应该在第二个数组中,反之亦然。

最快的方法是对数组进行排序并比较它们。

例如:

NSArray *array1=@[@"a",@"b",@"c"];
NSArray *array2=@[@"c",@"b",@"a"];

array1=[array1 sortedArrayUsingSelector:@selector(compare:)];
array2=[array2 sortedArrayUsingSelector:@selector(compare:)];

if ([array1 isEqualToArray:array2]) {
    NSLog(@"both have same elements");
}
else{
    NSLog(@"both having different elements");
}

【讨论】:

  • 哦,是的,我刚刚注意到...类似的想法。
  • @AKV 嗯,我分析并后来了解到它的最佳方法和替代方法也需要相同的时间。 :)
  • @FrankZp : 你也可以这样检查,但不建议模糊。
  • NSMutableArray *a1=[NSMutableArray arrayWithArray:array1]; NSMutableArray *a2=[NSMutableArray arrayWithArray:array2]; [a2 removeObjectsInArray:a1]; if ([a2 count]) { NSLog(@"两者相同"); } else{ NSLog(@"两者都不一样"); }
  • @AKV :感谢您的评论 - 但我认为我正在使用排序并与 isEqualToArray: 进行比较,因为这真的很好。
【解决方案2】:

如何将两个数组转换为集合并进行比较。

NSSet *set1 = [NSSet setWithArray:arr1];
NSSet *set2 = [NSSet setWithArray:arr2];

使用

比较两者
if([set1 isEqualToSet:set2]) {

}

【讨论】:

  • 我也考虑过这种方式,但是如果我评论的条件成立就不行了。
  • 你领先一步@borrden :)
  • 如果您不关心数组中同一对象的多个实例,这是正确的答案。
【解决方案3】:

使用 containsObject: 方法而不是迭代整个数组。

NSArray *array;
array = [NSArray arrayWithObjects: @"Nicola", @"Margherita",                                       @"Luciano", @"Silvia", nil];
if ([array containsObject: @"Nicola"]) // YES
  {
    // Do something
  }

喜欢这个

+ (BOOL)arraysContainSameObjects:(NSArray *)array1 andOtherArray:(NSArray *)array2 {
    // quit if array count is different
    if ([array1 count] != [array2 count]) return NO;

    BOOL bothArraysContainTheSameObjects = YES;

    for (id objectInArray1 in array1) {

        if (![array2 containsObject:objectInArray1])
        {
            bothArraysContainTheSameObjects = NO;
            break;
        }

    }

    return bothArraysContainTheSameObjects;
}

【讨论】:

  • containsObject 的复杂性在最坏的情况下通常是线性的(即使对数组进行了排序),因此这并不比两个嵌套的快速枚举渐近更有效。
  • @DanielMartín 嗯,我同意。正如 borrrden 所说,唯一的选择是排序和比较。
  • 但是,如果数组很可能不相等,这种方法可能会更快退出。
【解决方案4】:

试图让接受的答案起作用,但它并不是最适合我的情况。

我找到了this answer,所有功劳归于@joel kravets。

基本上使用比较器进行排序可以让您更轻松地使用对象进行排序 - 因此我在尝试使用上述解决方案时遇到了问题。

NSArray * array1 = [NSArray arrayWithArray:users];
NSArray * array2 = [NSArray arrayWithArray:threadUsers];

id mySort = ^(BUser * user1, BUser * user2){
    return [user1.name compare:user2.name];
};

array1 = [array1 sortedArrayUsingComparator:mySort];
array2 = [array2 sortedArrayUsingComparator:mySort];

if ([array1 isEqualToArray:array2]) {
    NSLog(@"both are same");
}
else{
    NSLog(@"both are different");
}

以前我曾尝试使用类似上述的其他答案,使用 break 来遍历循环,但最后这个答案最容易出现,可能是因为它的速度,而且最后我们有 if 语句允许我们放代码取决于它们是相同还是不同。

感谢 Anoop 让我走上正轨,感谢 Joel 帮助我提高效率

【讨论】:

    【解决方案5】:

    如果要检查两个数组是否包含相同的重复项,只需使用 NSCountedSet。它就像一个 NSSet,但集合中的每个对象也有一个计数,告诉你它被添加的频率。所以

    BOOL same = (array1.count == array2.count);
    if (same && array.count > 0)
    {
        NSCountedSet* set1 = [[NSCountedSet alloc] initWithArray:array1];
        NSCountedSet* set2 = [[NSCountedSet alloc] initWithArray:array2];
        same = ([set1 isEqual: set2]);
    }
    

    不管怎么做,都会比较费时间,所以可以考虑是否有特殊情况可以更快处理。这些数组通常是相同的,还是几乎相同的,或者它们在 99% 的情况下是不同的,并且在 99% 的情况下,array1 的随机元素不在 array2 中?数组经常排序吗?在这种情况下,您可以检查在相同位置是否有相同的对象,然后只考虑那些不相同的对象。如果一个数组包含对象 a、b、c、d、e,而另一个数组包含 a、b、x、d、y,则只需比较数组 [c, e] 与 [x, y]。

    【讨论】:

      【解决方案6】:
      [docTypes containsObject:@"Object"];
      

      它将适用于您的需求。尽快它会为它返回布尔值。

      【讨论】:

        【解决方案7】:

        这种方式的复杂度是 O(N^2),如果你按照这种方式,你就不能以较低的复杂度来做。相反,如果您对两个数组进行排序然后比较它们,则可以使用 O(N log(N)) 来完成。这样,在对它们进行排序后,您将在其他 N 操作中使用 isEqualToArray: 来完成。

        【讨论】:

        • 我想我必须对两个数组进行排序?如果第一个数组未排序而另一个数组已排序,我猜如果包含相同的对象,isEqualToArray 将不会返回 YES? (编辑:刚刚看到 borrrden 将此作为评论添加到第一篇文章中;))
        • 抱歉,我以为已经排序了,正在修复它。
        【解决方案8】:
        NSArray *filtered = [someArray filteredArrayUsingPredicate:[NSPredicate predicateWithFormat:@"someParamter == %@", paramValue]]];
        if (filtered.count) {
        
        }
        

        主要优点是您可以将它用于任何类型的对象:自定义、系统、NSDictionary。例如我需要知道我的 UINavigationController 的堆栈是否包含 MySearchResultsVC 和 MyTopMenuItemsVC:

            NSArray *filtered = [self.navigationController.viewControllers filteredArrayUsingPredicate:
                                             [NSPredicate predicateWithFormat:@"class IN %@",
                                              [NSArray arrayWithObjects:
                                               [MySearchResultsVC class],
                                               [MyTopMenuItemsVC class],
                                               nil]]];
        if (filtered) {
        /* ok, now we can handle it! */
        }
        

        【讨论】:

          【解决方案9】:

          我知道已经很晚了,但我只是想分享我的所作所为..

          NSString *stringArr1 = [NSString stringWithFormat:@"%@", array1];
          NSString *stringArr2 = [NSString stringWithFormat:@"%@", array2];
          
          if ([stringArr1 isEqual: stringArr2])
              NSLog(@"identical");
          else
              NSLog(@"not");
          

          这就像比较 "@[@1,@2,@3,@4]" == "[@3,@2,@1,@4]"..这显然是错误的..

          【讨论】:

            【解决方案10】:

            我想这样可以:

            [array1 isEqualToArray:array2];
            

            返回布尔值;

            【讨论】:

            • NSArray 类参考对isEqualToArray: 说:“如果两个数组都包含相同数量的对象并且每个数组中给定索引处的对象满足 isEqual: 测试,则它们具有相同的内容。” - 由于数组的排序方式不同,我想即使相同的对象在两个数组中但排序不同,该方法也会返回 NO。
            猜你喜欢
            • 1970-01-01
            • 2016-04-24
            • 1970-01-01
            • 2020-11-21
            • 1970-01-01
            • 1970-01-01
            • 2020-11-29
            • 1970-01-01
            • 1970-01-01
            相关资源
            最近更新 更多