【问题标题】:What's most performant way in iOS to check if a string is one of a list of strings?iOS中检查字符串是否是字符串列表之一的最高效方法是什么?
【发布时间】:2011-08-18 00:01:51
【问题描述】:

我想看看 stringA 是否等于字符串列表中的任何一个——string1、string2、string3。进行比较的最佳方式是什么?

由于我的比较列表很小,我目前正在尝试这个:

- (BOOL) isStringInList:(NSString *)testString{

if ([testString caseInsensitiveCompare:@"string1"] == NSOrderedSame)
   return YES;
else if ([testString caseInsensitiveCompare:@"string2"] == NSOrderedSame)
   return YES;
else if ([testString caseInsensitiveCompare:@"string3"] == NSOrderedSame)
   return YES;

return NO;
}

如果我有很多要比较的字符串,这显然不能很好地扩展。我更喜欢像-(BOOL) isString:(NSString *)testString inList:(NSString *)listString 这样的方法签名,其中listString 是一个以空格分隔的关键字字符串。

任何关于如何提高性能的想法都将不胜感激。

【问题讨论】:

    标签: iphone objective-c ios cocoa-touch performance


    【解决方案1】:

    最高效的方法是构造一个您要比较的字符串的NSSet,然后使用-member: 进行测试。一旦构建了集合,这将是一个恒定时间测试。如果您有一个以空格分隔的列表开头,则可以使用

    NSSet *set = [NSSet setWithArray:[listOfWords componentsSeparatedByString:@" "]]
    

    构造集合将与输入字符串的大小成线性关系。如果您的集合每次都相同,您可以构建一次并保留结果。要进行实际测试,您可以使用

    [set member:myWord]
    

    如果结果为零,则您的单词不在集合中。如果它不是零,它就是。请注意,这是一个区分大小写的搜索。如果您需要不区分大小写,那么您应该在执行测试之前将单词列表和输入单词都小写或大写。

    【讨论】:

    • 对使用集合匹配与 rangeOfString 的性能有何想法?
    • 我测试了这个的性能,还不如直接调用-containsObject:效率高。我怀疑+setWithArray: 引入了一些开销,而-containsObject: 也是常数时间。
    • @Harkonian: rangeOfString: 相对于空格分隔的列表将大致呈线性关系。它也不像 Davyd 看起来那么简单,因为您必须在字符串的开头和结尾都包含分隔符(在本例中为空格),并将它们附加到单词的开头和结尾,或者您结束解决我在对 Davyd 的评论中提到的问题。
    • @lemnar:如果您每次都在构建集合,那么您已经放弃了性能提升。但是,如果您坚持使用该装置并将其重新用于多次测试,那么它会更快。至于-containsObject:,如果您在谈论NSArray,那么您将回到线性时间查找。如果您的意思是-[NSSet containsObject:],那并没有明确记录它如何测试成员资格,但-member: 明确使用-isEqual:。如果-containsObject: 使用指针比较,那么这里就不合适了。
    • 我说的是-[NSArray containsObject:],它被记录为在每个对象上调用-isEqual:,这与使用指针比较的-containsObjectIdenticalTo: 不同。虽然这应该是线性时间,但我怀疑它依赖于-hash-isEqual: 必须给出功能等效结果的要求。因此,containsObject: 可以执行恒定时间查找。我确实认为不允许缓存列表。
    【解决方案2】:
    - (BOOL)isString:(NSString*)testString inList:(NSString*)listString
    {
        BOOL result = NO;
        if (testString != nil)
        {
            NSRange range = [listString rangeOfString:testString];
            result = (range.location != NSNotFound);
        }
        return result;
    }
    

    【讨论】:

    • 我觉得你有问题。 [self isString:@"book" inList:@"pen paper bookshelf"] 将返回 YES。
    • 为什么“book”不在“pen paper bookshelf”的范围内?
    • @Harkonian:会的。那就是问题所在。 “书”!=“书架”。
    • 这是经验上最快的方法。为使其正确,请在调用-rangeOfString: 之前将空格附加到listStringtestString
    • NSSet 解决方案更具可读性。这也是唯一的恒定时间解决方案。哪一个是正确的取决于您的用例。如果listString 可能会发生不可预测的变化,那么这个答案是正确的。如果您经常搜索相同的listString,则应将其缓存在NSSet 中,并获得恒定的时间搜索。
    【解决方案3】:
    - (BOOL)isString:(NSString *)testString inList:(NSString *)spaceSeparatedStrings
    {
        NSArray *list = [spaceSeparatedStrings componentsSeparatedByString:@" "];
        return [list containsObject:testString];
    }
    

    注意:与您的示例不同,上述内容区分大小写。对于不区分大小写的情况,您可能需要遍历 NSArray 的某些部分。

    【讨论】:

    • 我可以随时调高或调低两个传递的参数,而不必担心区分大小写。
    【解决方案4】:

    据此, containsObject 比我想象的要高效。好消息。

    二叉搜索树可能会更好,但显然,这需要一些工作。

    【讨论】:

    • 如果 NSArray 已排序,您可以通过很少的工作进行二进制搜索。请参阅indexOfObject:inSortedRange:options:usingComparator:NSBinarySearchingOptions 了解更多信息
    • 根据什么?这实际上是评论,而不是答案。多一点代表,you will be able to post comments.
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2019-10-03
    • 1970-01-01
    • 1970-01-01
    • 2021-12-28
    • 1970-01-01
    • 2019-09-24
    • 2013-02-13
    相关资源
    最近更新 更多