更好或更快:String.Contains vs List.Contains

时间:2017-12-15 00:13:11

标签: performance conventions

我知道这是一个愚蠢的问题,但我觉得有人可能想知道(或通知< redacted>同事)。我没有附加特定的编程语言,因为我认为它可以适用于所有这些语言。如果我错了,请纠正我。

主要问题:在常量StringList<String>中查找条目是否更快和/或更好?

详细信息:让我们说我想查看给定扩展名是否在受支持的扩展名列表中。以下哪项是最好的(关于编程风格)和/或最快:

const String SUPPORTED = ".exe.bin.sh.png.bmp" /*etc*/;

public static bool IsSupported(String ext){
    ext = Normalize(ext);//put the extension in some expected state like lowercase
    //String.Contains
    return SUPPORTED.Contains(ext);
}
final static List<String> SUPPORTED = MakeAnImmutableList(".exe", ".bin", ".sh",
      ".png",".bmp" /*etc*/);
public static bool IsSupported(String ext){
    ext = Normalize(ext);//put the extension in some expected state like lowercase
    //List.Contains
    return SUPPORTED.Contains(ext);
}

2 个答案:

答案 0 :(得分:2)

首先,重要的是要注意解决方案在功能上并不相同。对于xexe.bin等字符串,子字符串搜索将返回 true ,而List<String>.contains()则不会。从这个意义上讲,List<String>版本可能更接近您想要的语义。任何可能的性能比较都应牢记这一点。

现在,关于表现。

理论

从渐近和算法复杂性的角度来看,随着字符串长度的增长,List<String>.contains()方法将比另一种方法更快。从概念上讲,String.contains版本需要在SUPPORTED字符串中的每个位置中查找匹配,而List.contains()版本只需要在开始时匹配每个子字符串 - 一旦在当前候选中找到不匹配,它就会跳到下一个。这与上述注释相关,即选项在功能上并不等同:String.contains选项在理论上可以匹配更广泛的输入,因此在拒绝候选之前必须做更多的工作。

复杂性方面,如果您将N视为候选人数<{1}},O(N)List.contains() O(N^2)之间的区别可能类似String.contains() / em>并假设每个候选人都有一个有限的长度,并且通常的暴力中的String.contains()方法“寻找从每个位置开始的匹配”算法。事实证明,Java String.contains()实现并不完全是基本的O(N^2)搜索,但它也没有做Boyer-Moore。一般来说,一旦子串足够长,List.String方法就会更快。

关闭(r)金属

从更接近金属的角度来看,这两种方法都有其优势。 String.contains()解决方案避免了迭代List元素的开销:整个调用将用于内部化String.contains实现,而所有char构成整个调用SUPPORTED字符串是连续的,所以这是对内存友好的。 List.contains()方法会花费大量时间进行双重解除引用,从每个List元素到包含的String再到包含的char[]数组,如果您所比较的字符串很短,这可能会占主导地位。

另一方面,List.contains解决方案最终调用String.equals,这可能是以Arrays.equal(char[], char[])实现的,它在x86平台上使用SSE和AVX内在函数进行了大量优化,可能是即使与String.contains()的优化版本相比,速度也很快。因此,如果字符串变长,再次期望List.contains()继续前进。

所有这一切,有一种简单,规范的方法可以快速完成:HashSet<String>包含所有候选字符串。这只是一个简单的String.hash()(缓存,通常是“免费”)和哈希表中的单个查找。

答案 1 :(得分:1)

嗯,它可能因实现而异,但如果想以一般化的方式来看待这个问题,请看看。

如果你想在字符串中查找特定的子字符串,假设在包含不同扩展名的不可变字符串中的文件扩展名,你只需要遍历带有扩展名的字符串一次。

另一方面,使用不可变字符串列表,仍然需要遍历该列表中的每个字符串加上迭代该列表的开销。

作为结论,以一种通用的方式,您可以看到使用列表来存储字符串需要更多的处理。

但是,您可以通过其可读性,可维护性等来查看这两种解决方案。例如,如果要添加或删除新扩展或应用更复杂的操作,可能需要使用字符串列表来节省开销。