C# Contains比StartsWith快?
昨天来了一位顾问,不知怎的,他提到了弦的话题。他提到,他已经注意到,对于小于一定长度的字符串,C# Contains比StartsWith快?,c#,.net,performance,string,C#,.net,Performance,String,昨天来了一位顾问,不知怎的,他提到了弦的话题。他提到,他已经注意到,对于小于一定长度的字符串,Contains实际上比StartsWith更快。我必须用自己的两只眼睛看,所以我写了一个小应用程序,果然,包含的更快 这怎么可能 DateTime start = DateTime.MinValue; DateTime end = DateTime.MinValue; string str = "Hello there"; start = DateTime.Now; for (int i = 0;
Contains
实际上比StartsWith
更快。我必须用自己的两只眼睛看,所以我写了一个小应用程序,果然,包含的
更快
这怎么可能
DateTime start = DateTime.MinValue;
DateTime end = DateTime.MinValue;
string str = "Hello there";
start = DateTime.Now;
for (int i = 0; i < 10000000; i++)
{
str.Contains("H");
}
end = DateTime.Now;
Console.WriteLine("{0}ms using Contains", end.Subtract(start).Milliseconds);
start = DateTime.Now;
for (int i = 0; i < 10000000; i++)
{
str.StartsWith("H");
}
end = DateTime.Now;
Console.WriteLine("{0}ms using StartsWith", end.Subtract(start).Milliseconds);
我也试过用更长的弦 尝试使用
秒表
来测量速度,而不是日期时间
检查
我认为关键在于以下重要部分的加粗:
包含
:
此方法执行顺序
(区分大小写和
不区分区域性的比较
StartsWith
:
此方法执行一个字
(区分大小写和区分区域性)
使用当前区域性进行比较
我认为关键在于顺序比较,这相当于:
顺序排序比较基于字符串的排序
关于每个字符的数值
字符串中的对象。序数
比较是自动进行的
区分大小写,因为
以及字符的大写版本
有不同的代码点。然而,
如果案例在您的工作中不重要
应用程序中,可以指定
忽略大小写的顺序比较。
这相当于将
使用
不变的文化,然后执行
对结果的顺序比较
参考资料:
使用Reflector,您可以看到以下两个方面的代码:
public bool Contains(string value)
{
return (this.IndexOf(value, StringComparison.Ordinal) >= 0);
}
public bool StartsWith(string value, bool ignoreCase, CultureInfo culture)
{
if (value == null)
{
throw new ArgumentNullException("value");
}
if (this == value)
{
return true;
}
CultureInfo info = (culture == null) ? CultureInfo.CurrentCulture : culture;
return info.CompareInfo.IsPrefix(this, value,
ignoreCase ? CompareOptions.IgnoreCase : CompareOptions.None);
}
我想出来了。这是因为它对文化敏感,而不是。这就意味着
StartsWith
必须做更多的工作
FWIW,以下是我在Mono上的结果,基准如下(更正):
1988.7906ms using Contains
10174.1019ms using StartsWith
我很高兴看到人们在MS上的结果,但我的主要观点是正确地完成了(并假设进行了类似的优化),我认为StartsWith
必须要慢一些:
using System;
using System.Diagnostics;
public class ContainsStartsWith
{
public static void Main()
{
string str = "Hello there";
Stopwatch s = new Stopwatch();
s.Start();
for (int i = 0; i < 10000000; i++)
{
str.Contains("H");
}
s.Stop();
Console.WriteLine("{0}ms using Contains", s.Elapsed.TotalMilliseconds);
s.Reset();
s.Start();
for (int i = 0; i < 10000000; i++)
{
str.StartsWith("H");
}
s.Stop();
Console.WriteLine("{0}ms using StartsWith", s.Elapsed.TotalMilliseconds);
}
}
使用系统;
使用系统诊断;
公共类包含startswith
{
公共静态void Main()
{
string str=“你好”;
秒表s=新秒表();
s、 Start();
对于(int i=0;i<10000000;i++)
{
str.Contains(“H”);
}
s、 停止();
WriteLine(“{0}ms使用Contains”,s.appeased.total毫秒);
s、 重置();
s、 Start();
对于(int i=0;i<10000000;i++)
{
str.StartsWith(“H”);
}
s、 停止();
WriteLine(“{0}ms使用StartsWith”,s.appeased.total毫秒);
}
}
我在反射器里转来转去,找到了一个可能的答案:
包含:
return (this.IndexOf(value, StringComparison.Ordinal) >= 0);
开始于:
...
switch (comparisonType)
{
case StringComparison.CurrentCulture:
return CultureInfo.CurrentCulture.CompareInfo.IsPrefix(this, value, CompareOptions.None);
case StringComparison.CurrentCultureIgnoreCase:
return CultureInfo.CurrentCulture.CompareInfo.IsPrefix(this, value, CompareOptions.IgnoreCase);
case StringComparison.InvariantCulture:
return CultureInfo.InvariantCulture.CompareInfo.IsPrefix(this, value, CompareOptions.None);
case StringComparison.InvariantCultureIgnoreCase:
return CultureInfo.InvariantCulture.CompareInfo.IsPrefix(this, value, CompareOptions.IgnoreCase);
case StringComparison.Ordinal:
return ((this.Length >= value.Length) && (nativeCompareOrdinalEx(this, 0, value, 0, value.Length) == 0));
case StringComparison.OrdinalIgnoreCase:
return ((this.Length >= value.Length) && (TextInfo.CompareOrdinalIgnoreCaseEx(this, 0, value, 0, value.Length, value.Length) == 0));
}
throw new ArgumentException(Environment.GetResourceString("NotSupported_StringComparison"), "comparisonType");
还有一些重载,因此默认区域性是CurrentCulture
所以首先,序数会更快(如果字符串接近开头),对吗?第二,这里有更多的逻辑,可能会使事情变得缓慢(尽管如此琐碎)
StartsWith
和Contains
在涉及文化敏感问题时表现完全不同
特别是,StartsWith
返回true
并不意味着包含返回true
。只有当你真的知道自己在做什么时,你才应该用另一个替换其中的一个
using System;
class Program
{
static void Main()
{
var x = "A";
var y = "A\u0640";
Console.WriteLine(x.StartsWith(y)); // True
Console.WriteLine(x.Contains(y)); // False
}
}
让我们看看ILSpy对这两个人说了什么
public virtual int IndexOf(string source, string value, int startIndex, int count, CompareOptions options)
{
if (source == null)
{
throw new ArgumentNullException("source");
}
if (value == null)
{
throw new ArgumentNullException("value");
}
if (startIndex > source.Length)
{
throw new ArgumentOutOfRangeException("startIndex", Environment.GetResourceString("ArgumentOutOfRange_Index"));
}
if (source.Length == 0)
{
if (value.Length == 0)
{
return 0;
}
return -1;
}
else
{
if (startIndex < 0)
{
throw new ArgumentOutOfRangeException("startIndex", Environment.GetResourceString("ArgumentOutOfRange_Index"));
}
if (count < 0 || startIndex > source.Length - count)
{
throw new ArgumentOutOfRangeException("count", Environment.GetResourceString("ArgumentOutOfRange_Count"));
}
if (options == CompareOptions.OrdinalIgnoreCase)
{
return source.IndexOf(value, startIndex, count, StringComparison.OrdinalIgnoreCase);
}
if ((options & ~(CompareOptions.IgnoreCase | CompareOptions.IgnoreNonSpace | CompareOptions.IgnoreSymbols | CompareOptions.IgnoreKanaType | CompareOptions.IgnoreWidth)) != CompareOptions.None && options != CompareOptions.Ordinal)
{
throw new ArgumentException(Environment.GetResourceString("Argument_InvalidFlag"), "options");
}
return CompareInfo.InternalFindNLSStringEx(this.m_dataHandle, this.m_handleOrigin, this.m_sortName, CompareInfo.GetNativeCompareFlags(options) | 4194304 | ((source.IsAscii() && value.IsAscii()) ? 536870912 : 0), source, count, startIndex, value, value.Length);
}
}
public virtual int IndexOf(字符串源、字符串值、int startIndex、int计数、比较选项)
{
if(source==null)
{
抛出新的ArgumentNullException(“源”);
}
如果(值==null)
{
抛出新的ArgumentNullException(“值”);
}
如果(startIndex>source.Length)
{
抛出新ArgumentOutOfRangeException(“startIndex”,Environment.GetResourceString(“ArgumentOutOfRange_索引”);
}
if(source.Length==0)
{
如果(value.Length==0)
{
返回0;
}
返回-1;
}
其他的
{
如果(起始索引<0)
{
抛出新ArgumentOutOfRangeException(“startIndex”,Environment.GetResourceString(“ArgumentOutOfRange_索引”);
}
if(count<0 | | startIndex>source.Length-count)
{
抛出新ArgumentOutOfRangeException(“count”,Environment.GetResourceString(“ArgumentOutOfRange\u count”);
}
if(options==CompareOptions.OrdinalIgnoreCase)
{
返回source.IndexOf(value,startIndex,count,StringComparison.OrdinalIgnoreCase);
}
if((options&~(CompareOptions.IgnoreCase | CompareOptions.IgnoreNonSpace | CompareOptions.IgnoreSymbols | CompareOptions.IgnoreKanaType | CompareOptions.IgnoreWidth))!=CompareOptions.None&&options!=CompareOptions.Ordinal)
{
抛出新的ArgumentException(Environment.GetResourceString(“Argument_InvalidFlag”),“options”);
}
返回CompareInfo.InternalFindNLStringEx(this.m_dataHandle,this.m_handleOrigin,this.m_sortName,CompareInfo.GetNativeCompareFlags(options)| 4194304 |((source.IsAscii()&value.IsAscii())?536870912:0),source,count,startIndex,value,value,value.Length);
}
}
看起来它也考虑了文化,但它是默认的
public bool StartsWith(string value, StringComparison comparisonType)
{
if (value == null)
{
throw new ArgumentNullException("value");
}
if (comparisonType < StringComparison.CurrentCulture || comparisonType > StringComparison.OrdinalIgnoreCase)
{
throw new ArgumentException(Environment.GetResourceString("NotSupported_StringComparison"), "comparisonType");
}
if (this == value)
{
return true;
}
if (value.Length == 0)
{
return true;
}
switch (comparisonType)
{
case StringComparison.CurrentCulture:
return CultureInfo.CurrentCulture.CompareInfo.IsPrefix(this, value, CompareOptions.None);
case StringComparison.CurrentCultureIgnoreCase:
return CultureInfo.CurrentCulture.CompareInfo.IsPrefix(this, value, CompareOptions.IgnoreCase);
case StringComparison.InvariantCulture:
return CultureInfo.InvariantCulture.CompareInfo.IsPrefix(this, value, CompareOptions.None);
case StringComparison.InvariantCultureIgnoreCase:
return CultureInfo.InvariantCulture.CompareInfo.IsPrefix(this, value, CompareOptions.IgnoreCase);
case StringComparison.Ordinal:
return this.Length >= value.Length && string.nativeCompareOrdinalEx(this, 0, value, 0, value.Length) == 0;
case StringComparison.OrdinalIgnoreCase:
return this.Length >= value.Length && TextInfo.CompareOrdinalIgnoreCaseEx(this, 0, value, 0, value.Length, value.Length) == 0;
default:
throw new ArgumentException(Environment.GetResourceString("NotSupported_StringComparison"), "comparisonType");
}
public bool StartsWith(字符串值、字符串比较类型)
{
如果(值==null)
{
抛出新的ArgumentNullException(“值”);
}
if(comparisonTypeStringComparison.OrdinalIgnoreCase)
{
抛出新的ArgumentException(Environment.GetResourceString(“NotSupported_StringComparison”),“comparisonType”);
}
if(this==值)
{
返回true;
}
如果(value.Length==0)
{
返回true;
}
开关(比较类型)
{
case StringComparison.CurrentCultu
public bool StartsWith(string value, StringComparison comparisonType)
{
if (value == null)
{
throw new ArgumentNullException("value");
}
if (comparisonType < StringComparison.CurrentCulture || comparisonType > StringComparison.OrdinalIgnoreCase)
{
throw new ArgumentException(Environment.GetResourceString("NotSupported_StringComparison"), "comparisonType");
}
if (this == value)
{
return true;
}
if (value.Length == 0)
{
return true;
}
switch (comparisonType)
{
case StringComparison.CurrentCulture:
return CultureInfo.CurrentCulture.CompareInfo.IsPrefix(this, value, CompareOptions.None);
case StringComparison.CurrentCultureIgnoreCase:
return CultureInfo.CurrentCulture.CompareInfo.IsPrefix(this, value, CompareOptions.IgnoreCase);
case StringComparison.InvariantCulture:
return CultureInfo.InvariantCulture.CompareInfo.IsPrefix(this, value, CompareOptions.None);
case StringComparison.InvariantCultureIgnoreCase:
return CultureInfo.InvariantCulture.CompareInfo.IsPrefix(this, value, CompareOptions.IgnoreCase);
case StringComparison.Ordinal:
return this.Length >= value.Length && string.nativeCompareOrdinalEx(this, 0, value, 0, value.Length) == 0;
case StringComparison.OrdinalIgnoreCase:
return this.Length >= value.Length && TextInfo.CompareOrdinalIgnoreCaseEx(this, 0, value, 0, value.Length, value.Length) == 0;
default:
throw new ArgumentException(Environment.GetResourceString("NotSupported_StringComparison"), "comparisonType");
}
| Method | Mean | Error | StdDev | Median | Gen 0 | Gen 1 | Gen 2 | Allocated |
|----------------------------------------- |-------------:|-----------:|-------------:|-------------:|----------:|------:|------:|----------:|
| EnumEqualsMethod | 1,079.67 us | 43.707 us | 114.373 us | 1,059.98 us | 1019.5313 | - | - | 4800000 B |
| EnumEqualsOp | 28.15 us | 0.533 us | 0.547 us | 28.34 us | - | - | - | - |
| ContainsName | 1,572.15 us | 152.347 us | 449.198 us | 1,639.93 us | - | - | - | - |
| ContainsShortName | 1,771.03 us | 103.982 us | 306.592 us | 1,749.32 us | - | - | - | - |
| StartsWithName | 14,511.94 us | 764.825 us | 2,255.103 us | 14,592.07 us | - | - | - | - |
| StartsWithNameOrdinalComp | 1,147.03 us | 32.467 us | 93.674 us | 1,153.34 us | - | - | - | - |
| StartsWithNameOrdinalCompIgnoreCase | 1,519.30 us | 134.951 us | 397.907 us | 1,264.27 us | - | - | - | - |
| StartsWithShortName | 7,140.82 us | 61.513 us | 51.366 us | 7,133.75 us | - | - | - | 4 B |
| StartsWithShortNameOrdinalComp | 970.83 us | 68.742 us | 202.686 us | 1,019.14 us | - | - | - | - |
| StartsWithShortNameOrdinalCompIgnoreCase | 802.22 us | 15.975 us | 32.270 us | 792.46 us | - | - | - | - |
| EqualsSubstringOrdinalCompShortName | 4,578.37 us | 91.567 us | 231.402 us | 4,588.09 us | 679.6875 | - | - | 3200000 B |
| EqualsOpShortNametoCharArray | 1,937.55 us | 53.821 us | 145.508 us | 1,901.96 us | 1695.3125 | - | - | 8000000 B |