C#中字符串类型最快的内置比较方法是什么?我不介意印刷/语义含义:目的是在排序列表中使用比较器,以便在大型集合中快速搜索。我认为只有两种方法:Compare
和CompareOrdinal
。什么是最快的?
此外,这些字符串比较是否有更快的方法?
答案 0 :(得分:57)
我假设你想要一个小于/等于/大于比较而不仅仅是平等;平等是一个略有不同的主题,虽然原则基本相同。如果您实际上只在SortedList
之类的内容中搜索状态,我会考虑使用Dictionary<string, XXX>
- 您真的需要所有排序吗?
String.CompareOrdinal
,或使用允许提供比较的String.Compare
重载,并指定序数(区分大小写)比较,例如String.Compare(x, y, StringComparison.Ordinal)
将是最快的。
基本上,序数比较只需需要逐个字符地处理两个字符串,直到找到差异为止。如果没有发现任何差异,并且长度相同,则结果为0.如果没有发现任何差异但长度不同,则较长的字符串被视为“较大”。如果它 找到差异,它可以立即解决,根据顺序术语中哪个字符“更大”,被视为“更大”。
放置是另一种方式:它就像在两个char[]
值之间进行明显的比较。
文化敏感的比较必须根据您使用的精确文化执行各种曲折的壮举。有关此示例,请参阅this question。很明显,遵循更复杂的规则会使速度变慢。
答案 1 :(得分:11)
我刚刚注意到我自己的代码性能提高了50%,首先比较字符串长度,如果相等,那么使用string.compare方法。所以在循环中我有:
<强> 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
}
}
这可能取决于你自己的字符串,但它似乎对我有用。
答案 2 :(得分:4)
最快的是使用引用相等性测试的实习字符串,但是你只能获得相等的测试,并且它需要花费大量的内存 - 如此昂贵以至于它几乎不是推荐的课程。
过去,一个区分大小写的序数测试将是最快的,并且对于非特定于文化的字符串,绝对推荐使用此方法。如果它适用于您的用例,则区分大小写会更快。
当您指定
StringComparison.Ordinal
或StringComparison.OrdinalIgnoreCase
时,字符串比较将是非语言的。也就是说,在进行比较决策时,将忽略特定于自然语言的特征。这意味着决策基于简单的字节比较,并忽略由文化参数化的套管或等价表。 因此,通过明确地将参数设置为StringComparison.Ordinal
或StringComparison.OrdinalIgnoreCase
,您的代码通常会提高速度,提高正确性并变得更加可靠。
答案 3 :(得分:4)
我设计了一个单元测试来测试字符串比较速度,使用本文中提到的一些方法。此测试使用.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();
}
}
}
答案 4 :(得分:3)
这是一个很老的问题,但是因为我发现其他人也可能。
在进一步研究这个主题时,我遇到了interesting blog post,它比较了所有字符串比较方法。可能不是很科学,但仍然是一个很好的房子。
感谢这篇文章,我开始在一个场景中使用string.CompareOrdinal,我必须找出一个字符串是否在170.000个其他字符串的列表中并连续1600次执行此操作。 string.CompareOrdinal比string.Equals
快了近50%答案 5 :(得分:1)
我使用秒表
检查了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和不相等的字符串组合,发现每次CompareOrdinal都比仅比较更快..
这是我自己的观察..您也可以尝试在表单上放两个按钮,然后在重新分级事件中复制粘贴此代码。
答案 6 :(得分:1)
这可能对某些人有用,但更改我的代码的一行会使我的方法的单元测试从140ms降低到1ms!
<强>原始强>
单元测试: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方法花费140ms的唯一单元测试。 StringsMatch_AllParamsNullOrEmpty_ReturnTrue()始终为1ms,StringsMatch_OnlyString2NullOrEmpty_ReturnFalse()始终<1ms。
答案 7 :(得分:0)
我认为大多数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))
因此,请随意切片并骰子找到自己的做事方式。请记住,虽然有人必须维护代码,但可能不想花时间试图弄清楚为什么要使用你决定使用的任何方法。
与往常一样,优化您自己的风险。 : - )