C#中字符串类型的最快(内置)比较是什么

时间:2009-09-20 21:20:41

标签: c# performance string

C#中字符串类型最快的内置比较方法是什么?我不介意印刷/语义含义:目的是在排序列表中使用比较器,以便在大型集合中快速搜索。我认为只有两种方法:CompareCompareOrdinal。什么是最快的?

此外,这些字符串比较是否有更快的方法?

8 个答案:

答案 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.OrdinalStringComparison.OrdinalIgnoreCase时,字符串比较将是非语言的。也就是说,在进行比较决策时,将忽略特定于自然语言的特征。这意味着决策基于简单的字节比较,并忽略由文化参数化的套管或等价表。 因此,通过明确地将参数设置为StringComparison.OrdinalStringComparison.OrdinalIgnoreCase,您的代码通常会提高速度,提高正确性并变得更加可靠。

Source

答案 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
  • 编写自定义算法以通过char
  • 比较char

如果你想走极端,你可以使用其他不那么明显的对象/方法:

  • 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))

因此,请随意切片并骰子找到自己的做事方式。请记住,虽然有人必须维护代码,但可能不想花时间试图弄清楚为什么要使用你决定使用的任何方法。

与往常一样,优化您自己的风险。 : - )