C#静态字符串[]contains()的性能(slooow)vs.==运算符

C#静态字符串[]contains()的性能(slooow)vs.==运算符,c#,C#,只是一个快速查询: 我有一段代码,将一个字符串与一长串值进行比较,例如 if(str == "string1" || str == "string2" || str == "string3" || str == "string4". DoSomething(); 出于代码清晰性和可维护性的考虑,我将其改为 public static string[] strValues = { "String1", "String2", "String3", "String4"}; ... if(s

只是一个快速查询: 我有一段代码,将一个字符串与一长串值进行比较,例如

if(str == "string1" || str == "string2" || str == "string3" || str == "string4".
     DoSomething();
出于代码清晰性和可维护性的考虑,我将其改为

public static string[] strValues = { "String1", "String2", "String3", "String4"};
...
if(strValues.Contains(str)
    DoSomething();
只发现代码执行时间从2.5秒变为6.8秒(执行约200000次)。
我当然理解一个轻微的性能权衡,但是300%?
无论如何,我可以用不同的方式定义静态字符串以提高性能?

干杯。

您尝试的两种方法都具有O(n)性能,因此当您添加更多字符串时,它们的速度会变慢。如果您使用的是.NET 3.5或更高版本,则可以尝试改用,并在应用程序启动时初始化一次。然后您可以得到大约0(1)个查找


对于.NET v2.0,您可以使用
字典和
ContainsKey来模拟
哈希集
,而不使用该值。

您在生产代码中实际运行了200000次吗?如果需要的话,你可能想把哈希检查看作是一个更快的否定检查。 如果20万次只是为了说明差异,那么我就不会担心了。时间只增加了0.02毫秒


Contains
比测试静态字符串更通用,因此有少量开销。除非这段代码是Mark提到的瓶颈,否则它不值得优化。CS中有一句著名的格言:“过早优化是万恶之源。”这句格言并不十分准确,但它很好地提醒了我们终极优化准则:首先测量。

您可能会发现Contains()适用于更长的列表。例如,它可能会对列表排序并进行二进制搜索(例如,只是一个思维实验)。

这里有一个替代方案,您可能会发现它可读且可维护,您可能希望测试它的速度。如果你测试了它的速度,请发布你的结果

        switch (str)
        {
            case "String1":
            case "String2":
            case "String3":
            case "String4":
                DoSomething();
                break;
        }

尽管按照建议使用
HashSet
可能是一个更好的选择,但
strValues.Contains(str)
之所以较慢,是因为它是一种通用扩展方法。数组上没有Contains方法

它对阵列的工作方式基本上是

if (strValues is ICollection<string>) // true
{
    return ((ICollection<string>) strValues).Contains(str);
}
if(strValues是ICollection)//true
{
返回((ICollection)strValues).Contains(str);
}
它添加了类型检查、类型转换和虚拟调用。然后它将迭代数组(导致边界检查)。只有到那时,才会进行字符串比较。所以它做了更多的工作

请注意,在C#3中(如果使用扩展方法,则必须使用扩展方法),只需像下面这样初始化
HashSet

public static HashSet<string> strValues = new HashSet<string> { 
                                   "String1", "String2", "String3", "String4" };
publicstatichashset strValues=newhashset{
“String1”、“String2”、“String3”、“String4”};
这使您的程序与现在使用数组时一样可读。

Fyi

使用:

private static HashSet<string> strHashSet = new HashSet<string>() 
{ "0string", "1string", "2string", "3string", "4string", "5string", 
  "6string", "7string", "8string", "9string", "Astring", "Bstring" };

private static List<string> strList = strHashSet.ToList();
private static string[] strArray = strList.ToArray();
private static Dictionary<int, string> strHashDict = strHashSet.ToDictionary(h => h.GetHashCode());
private static Dictionary<string, string> strDict = strHashSet.ToDictionary(h => h);

// Only one test uses this method.
private static bool ExistsInList(string str)
{
  return strHashDict.ContainsKey(str.GetHashCode());
}
当我们更改列表中的字符串时,会出现有趣的(如果不是意外的)结果:

private static HashSet<string> strHashSet = new HashSet<string>() 
{ "string00", "string01", "string02", "string03", "string04", "string05", 
  "string06", "string07", "string08", "string09", "string10", "string11" };
对于案例A和B,测试2和3具有优势

但是,HashSet.Contains(string)非常有效,不受列表内容的影响,并且具有清晰的语法…可能是最佳选择


是的,这是真的,我没有生命。

这部分代码是你应用程序的瓶颈吗?@mark,这有关系吗?问题是如何获得更高的性能,这本身就是一个很好的问题。“一长串的值”:多长是“长”?列表中有多少字符串?大约12个值。“long2”部分其实并不重要,只是性能上的差异。对不起,你错了一点。我认为framework1.x的最佳选择是
System.Collections.Hashtable
。因为1.x没有泛型@阿巴蒂什切夫:你说得对,1.x没有泛型。但我觉得,假设人们现在不使用.NET1.x是可以的,除非他们在问题中提到它。话虽如此,我已经改写了我的答案,让它更清楚。我刚刚做了一个实验,哈希集的速度与ifs相同。@Keith Nicholas:对不起,什么是
ifs
?啊。。如果
,则有大量的
。:)@abatishchev:最初的问题在C#3/.NET3.5中:Contains方法是一个扩展方法(数组类上没有公共Contains方法)。因此,使用
HashSet
应该不是问题。这一切都很好,但如果使用更有效的替代方法同样容易,那么就不应该浪费时间。用HashSet替换字符串[]更有效,也更简洁。通过这种方式,默认情况下会选择高效的机制,无论它们是否真正起作用。我发现,清晰易读的代码比在实际场景中使用时对运行时影响小得无法察觉的微优化投入大量工作要省时得多。当然。。。但正如我所说的,如果您可以选择具有同等可读性的更有效的默认值,那么您应该这样做。过早优化的建议主要是在构建系统的背景下提出的,有利于可能永远不会有回报的性能。问题是,人们现在用它作为一个借口,当他们能做到的时候,通常不高效。用hashset替换字符串[]不会引起结构上的变化。据我所记得的,如果案例数超过4,C#编译器会自动为开关大小写块创建某种哈希表。要编译它,需要在
DoSomething()
之后使用
break
。不幸的是,我们不能做出这样的假设。它总是对数组进行线性搜索。
private static HashSet<string> strHashSet = new HashSet<string>() 
{ "string00", "string01", "string02", "string03", "string04", "string05", 
  "string06", "string07", "string08", "string09", "string10", "string11" };
1.B Test: result = (str == "string00" || str == "string01" ...
[storage var]          [first]:[ last ]:[ none ]:[average]
strArray                85.45 :  87.06 :  91.82 :  88.11

2.B Test: ExistsInList(string);
[storage var]          [first]:[ last ]:[ none ]:[average]
none                    30.12 :  27.97 :  21.36 :  26.48

3.B Test: .ContainsKey(string.GetHashCode());
[storage var]          [first]:[ last ]:[ none ]:[average]
strHashDict             32.51 :  28.00 :  20.83 :  27.11

4.B Test: .ContainsKey(string);
[storage var]          [first]:[ last ]:[ none ]:[average]
strDict                 36.45 :  32.13 :  22.39 :  30.32

5.B Test: .Contains(string);
[storage var]          [first]:[ last ]:[ none ]:[average]
strHashSet              37.29 :  34.33 :  23.56 :  31.73
strList                 23.34 : 147.75 : 153.04 : 108.04
strArray               349.62 : 460.19 : 459.99 : 423.26

6.B Test: .Any(p => p == string);
[storage var]          [first]:[ last ]:[ none ]:[average]
strHashSet              76.26 : 355.09 : 361.31 : 264.22
strList                 70.20 : 332.33 : 341.79 : 248.11
strArray                37.23 : 234.70 : 251.81 : 174.58