【发布时间】:2010-11-29 22:30:27
【问题描述】:
C# 中最快的字符串类型内置比较方法是什么?我不介意印刷/语义含义:目的是在排序列表中使用比较器,以便在大型集合中快速搜索。我认为只有两种方法:Compare 和CompareOrdinal。什么速度最快?
另外,那些字符串比较有没有更快的方法?
【问题讨论】:
标签: c# performance string
C# 中最快的字符串类型内置比较方法是什么?我不介意印刷/语义含义:目的是在排序列表中使用比较器,以便在大型集合中快速搜索。我认为只有两种方法:Compare 和CompareOrdinal。什么速度最快?
另外,那些字符串比较有没有更快的方法?
【问题讨论】:
标签: c# performance string
我假设您想要一个小于/等于/大于的比较,而不仅仅是相等;平等是一个稍微不同的话题,尽管原则基本相同。如果您实际上只是在SortedList 之类的地方搜索存在,我会考虑改用Dictionary<string, XXX> - 您真的需要所有这些排序吗?
String.CompareOrdinal,或使用允许提供比较的String.Compare 的重载,并指定一个序数(区分大小写)比较,例如String.Compare(x, y, StringComparison.Ordinal) 将是最快的。
基本上,序数比较只是需要逐个字符地遍历两个字符串,直到找到差异。如果没有发现任何差异,并且长度相同,则结果为 0。如果没有发现任何差异但长度不同,则认为较长的字符串“较大”。如果它确实发现了差异,它可以立即根据哪个字符在序数中“更大”来确定哪个被认为“更大”。
换一种说法:这就像在两个char[] 值之间进行明显的比较。
文化敏感的比较必须执行各种曲折的壮举,具体取决于您使用的精确文化。有关这方面的示例,请参阅this question。很明显,遵循更复杂的规则会使这个过程变慢。
【讨论】:
String.Compare(x, y, StringCompare.Ordinal) 和String.CompareOrdinal(x, y) 有区别吗?还是其中一个只是打电话给另一个?
最快的是带引用相等性测试的实习字符串,但您只能进行相等性测试,而且会耗费大量内存 - 如此昂贵,几乎从来都不是推荐的课程。
除此之外,区分大小写的序数测试将是最快的,并且绝对建议将这种方法用于非文化特定的字符串。如果适用于您的用例,则区分大小写会更快。
当您指定
StringComparison.Ordinal或StringComparison.OrdinalIgnoreCase时,字符串比较将是非语言的。也就是说,在做出比较决策时会忽略特定于自然语言的特征。这意味着决策基于简单的字节比较并忽略由文化参数化的大小写或等价表。 因此,通过将参数显式设置为StringComparison.Ordinal或StringComparison.OrdinalIgnoreCase,您的代码通常会加快速度、提高正确性并变得更加可靠。
【讨论】:
我使用秒表检查了 string.Compare 和 string.CompareOrdinal
--Compare Ordinal case 1
Stopwatch sw = new Stopwatch();
sw.Start();
int x = string.CompareOrdinal("Jaswant Agarwal", "Jaswant Agarwal");
sw.Stop();
lblTimeGap.Text = sw.Elapsed.ToString();
-- Only compare case 2
Stopwatch sw = new Stopwatch();
sw.Start();
int x = string.Compare("Jaswant Agarwal", "Jaswant Agarwal");
sw.Stop();
lblTimeGap.Text = sw.Elapsed.ToString();
如果 1 平均经过的时间是 00:00:00.0000030 案例 2 平均经过的时间是 00:00:00.0000086
我尝试了不同的 Equal 和 not equal 字符串组合,发现每次 CompareOrdinal 都比只比较快..
这是我自己的观察..您也可以尝试在表单上放置两个按钮,然后将此代码复制粘贴到重新评分事件中..
【讨论】:
我设计了一个单元测试来使用本文中提到的一些方法来测试字符串比较速度。该测试使用 .NET 4 运行
简而言之,差别不大,我必须进行 100,000,000 次迭代才能看到显着差异。由于似乎是依次比较字符直到发现差异,因此不可避免地会影响字符串的相似程度。
这些结果实际上似乎表明使用 str1.Equals(str2) 是比较字符串的最快方法。
这些是测试的结果,包括测试类:
######## SET 1 compared strings are the same: 0
#### Basic == compare: 413
#### Equals compare: 355
#### Equals(compare2, StringComparison.Ordinal) compare: 387
#### String.Compare(compare1, compare2, StringComparison.Ordinal) compare: 426
#### String.CompareOrdinal(compare1, compare2) compare: 412
######## SET 2 compared strings are NOT the same: 0
#### Basic == compare: 710
#### Equals compare: 733
#### Equals(compare2, StringComparison.Ordinal) compare: 840
#### String.Compare(compare1, compare2, StringComparison.Ordinal) compare: 987
#### String.CompareOrdinal(compare1, compare2) compare: 776
using System;
using System.Diagnostics;
using NUnit.Framework;
namespace Fwr.UnitTests
{
[TestFixture]
public class StringTests
{
[Test]
public void Test_fast_string_compare()
{
int iterations = 100000000;
bool result = false;
var stopWatch = new Stopwatch();
Debug.WriteLine("######## SET 1 compared strings are the same: " + stopWatch.ElapsedMilliseconds);
string compare1 = "xxxxxxxxxxxxxxxxxx";
string compare2 = "xxxxxxxxxxxxxxxxxx";
// Test 1
stopWatch.Start();
for (int i = 0; i < iterations; i++)
{
result = compare1 == compare2;
}
stopWatch.Stop();
Debug.WriteLine("#### Basic == compare: " + stopWatch.ElapsedMilliseconds);
stopWatch.Reset();
// Test 2
stopWatch.Start();
for (int i = 0; i < iterations; i++)
{
result = compare1.Equals(compare2);
}
stopWatch.Stop();
Debug.WriteLine("#### Equals compare: " + stopWatch.ElapsedMilliseconds);
stopWatch.Reset();
// Test 3
stopWatch.Start();
for (int i = 0; i < iterations; i++)
{
result = compare1.Equals(compare2, StringComparison.Ordinal);
}
stopWatch.Stop();
Debug.WriteLine("#### Equals(compare2, StringComparison.Ordinal) compare: " + stopWatch.ElapsedMilliseconds);
stopWatch.Reset();
// Test 4
stopWatch.Start();
for (int i = 0; i < iterations; i++)
{
result = String.Compare(compare1, compare2, StringComparison.Ordinal) != 0;
}
stopWatch.Stop();
Debug.WriteLine("#### String.Compare(compare1, compare2, StringComparison.Ordinal) compare: " + stopWatch.ElapsedMilliseconds);
stopWatch.Reset();
// Test 5
stopWatch.Start();
for (int i = 0; i < iterations; i++)
{
result = String.CompareOrdinal(compare1, compare2) != 0;
}
stopWatch.Stop();
Debug.WriteLine("#### String.CompareOrdinal(compare1, compare2) compare: " + stopWatch.ElapsedMilliseconds);
stopWatch.Reset();
Debug.WriteLine("######## SET 2 compared strings are NOT the same: " + stopWatch.ElapsedMilliseconds);
compare1 = "ueoqwwnsdlkskjsowy";
compare2 = "sakjdjsjahsdhsjdak";
// Test 1
stopWatch.Start();
for (int i = 0; i < iterations; i++)
{
result = compare1 == compare2;
}
stopWatch.Stop();
Debug.WriteLine("#### Basic == compare: " + stopWatch.ElapsedMilliseconds);
stopWatch.Reset();
// Test 2
stopWatch.Start();
for (int i = 0; i < iterations; i++)
{
result = compare1.Equals(compare2);
}
stopWatch.Stop();
Debug.WriteLine("#### Equals compare: " + stopWatch.ElapsedMilliseconds);
stopWatch.Reset();
// Test 3
stopWatch.Start();
for (int i = 0; i < iterations; i++)
{
result = compare1.Equals(compare2, StringComparison.Ordinal);
}
stopWatch.Stop();
Debug.WriteLine("#### Equals(compare2, StringComparison.Ordinal) compare: " + stopWatch.ElapsedMilliseconds);
stopWatch.Reset();
// Test 4
stopWatch.Start();
for (int i = 0; i < iterations; i++)
{
result = String.Compare(compare1, compare2, StringComparison.Ordinal) != 0;
}
stopWatch.Stop();
Debug.WriteLine("#### String.Compare(compare1, compare2, StringComparison.Ordinal) compare: " + stopWatch.ElapsedMilliseconds);
stopWatch.Reset();
// Test 5
stopWatch.Start();
for (int i = 0; i < iterations; i++)
{
result = String.CompareOrdinal(compare1, compare2) != 0;
}
stopWatch.Stop();
Debug.WriteLine("#### String.CompareOrdinal(compare1, compare2) compare: " + stopWatch.ElapsedMilliseconds);
stopWatch.Reset();
}
}
}
【讨论】:
我刚刚注意到通过首先比较字符串长度,如果相等,然后使用 string.compare 方法,我自己的代码的性能提高了 50%。所以在一个循环中我有:
VB:
If strA.length = strB.length then
if string.compare(strA,strB,true) = 0 then
TheyAreEqual
End if
End if
C#:
if(strA.Length == strB.Length)
{
if(string.Compare(strA,strB,true) == 0)
{
//they are equal
}
}
这可能取决于您自己的字符串,但它似乎对我来说效果很好。
【讨论】:
if 语句 -- bool isEqual = strA.Length == strB.Length && string.Compare(strA, strB, true) == 0;。 VB.NET 还支持使用 AndAlso 和 OrElse 运算符的短路布尔逻辑 -- Dim isEquals = strA.Length = strB.Length AndAlso String.Compare(strA, strB, true) = 0。
这可能对某人有用,但是更改我的代码行使我的方法的单元测试从 140 毫秒缩短到 1 毫秒!
原创
单元测试:140ms
public bool StringsMatch(string string1, string string2)
{
if (string1 == null && string2 == null) return true;
return string1.Equals(string2, StringComparison.Ordinal);
}
新
单元测试:1ms
public bool StringsMatch(string string1, string string2)
{
if (string1 == null && string2 == null) return true;
return string.CompareOrdinal(string1, string2) == 0 ? true : false;
}
单元测试 (NUnit)
[Test]
public void StringsMatch_OnlyString1NullOrEmpty_ReturnFalse()
{
Authentication auth = new Authentication();
Assert.IsFalse(auth.StringsMatch(null, "foo"));
Assert.IsFalse(auth.StringsMatch("", "foo"));
}
有趣的是,StringsMatch_OnlyString1NullOrEmpty_ReturnFalse() 是 StringsMatch 方法中唯一需要 140 毫秒的单元测试。 StringsMatch_AllParamsNullOrEmpty_ReturnTrue() 始终为 1ms,StringsMatch_OnlyString2NullOrEmpty_ReturnFalse() 始终为
【讨论】:
这是一个相当古老的问题,但既然我发现了它,其他人也可能会这样做。
在进一步研究这个主题时,我发现了一个interesting blog post,它比较了所有字符串比较的方法。可能不是很科学,但仍然是一个很好的门牌号码。
感谢这篇文章,我开始在一个场景中使用 string.CompareOrdinal,我必须找出一个字符串是否在 170.000 个其他字符串的列表中,并且连续执行 1600 次。 string.CompareOrdinal 比 string.Equals 快了将近 50%
【讨论】:
if (hs.Contains(stringsWeWantToSeeIfMatches)) {}
我认为大多数 C# 开发人员都有几种比较字符串的方法,以下是最常见的:
Compare - 正如你所提到的CompareOrdinal - 正如你所提到的==String.Equals如果你想走极端,你可以使用其他不那么明显的对象/方法:
SequenceEqual 示例:
c1 = str1.ToCharArray();
c2 = str2.ToCharArray();
if (c1.SequenceEqual(c2))
IndexOf 示例:if (stringsWeAreComparingAgainst.IndexOf(stringsWeWantToSeeIfMatches, 0 , stringsWeWantToSeeIfMatches.Length) == 0)
或者您可以实现 Dictionary 和 HashSet,将字符串用作“键”并测试它们是否已经与您要比较的字符串一起存在。例如:if (hs.Contains(stringsWeWantToSeeIfMatches))
因此,您可以随意切磋琢磨,找到适合自己的做事方式。请记住,尽管有人将不得不维护代码,并且可能不想花时间试图弄清楚您为什么要使用您决定使用的任何方法。
与往常一样,优化风险自负。 :-)
【讨论】: