簡體   English   中英

C#中字符串類型的最快(內置)比較是什么

[英]What is the fastest (built-in) comparison for string-types in C#

C#中字符串類型最快的內置比較方法是什么? 我不介意印刷/語義含義:目的是在排序列表中使用比較器,以便在大型集合中快速搜索。 我認為只有兩種方法: CompareCompareOrdinal 什么是最快的?

另外,這些字符串比較是否有更快的方法?

我假設你想要一個小於/等於/大於比較而不僅僅是平等; 平等是一個略有不同的主題,雖然原則基本相同。 如果你實際上只在像一個搜索存在 SortedList ,我會考慮使用Dictionary<string, XXX>而是-你真的需要所有的排序?

String.CompareOrdinal ,或使用的過載String.Compare其允許提供的比較,並指定一個序號(區分大小寫)比較,例如String.Compare(x, y, StringComparison.Ordinal)將是最快的。

基本上,有序比較只需逐個字符地走兩個字符串,直到找到差異為止。 如果沒有發現任何差異,並且長度相同,則結果為0.如果沒有發現任何差異但長度不同,則較長的字符串被視為“較大”。 如果它確實找到了差異,它可以立即解決,根據順序術語中哪個字符“更大”,這被認為是“更大”。

放置是另一種方式:它就像在兩個char[]值之間進行明顯的比較。

文化敏感的比較必須根據您使用的精確文化執行各種曲折的壯舉。 有關此示例,請參閱此問題 很明顯,遵循更復雜的規則會使速度變慢。

我剛剛注意到我自己的代碼性能提高了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
   }
}

這可能取決於你自己的字符串,但它似乎對我有用。

我設計了一個單元測試來測試字符串比較速度,使用本文中提到的一些方法。 此測試使用.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();
        }
    }
}

最快的是帶有參考相等性測試的實習字符串,但是你只能獲得相同的測試,並且它需要花費大量的內存 - 如此昂貴以至於它幾乎不是推薦的課程

過去,一個區分大小寫的序數測試將是最快的,並且這種方法絕對推薦用於非特定文化的字符串。 如果它適用於您的用例,則區分大小寫更快。

指定StringComparison.OrdinalStringComparison.OrdinalIgnoreCase ,字符串比較將是非語言的。 也就是說,在進行比較決策時,將忽略特定於自然語言的特征。 這意味着決策基於簡單的字節比較,並忽略由文化參數化的套管或等價表。 因此,通過將參數顯式設置為StringComparison.OrdinalStringComparison.OrdinalIgnoreCase ,您的代碼通常可以提高速度 ,提高正確性並變得更加可靠。

資源

這是一個很老的問題,但是因為我發現其他人也可能。

在進一步研究這個主題時,我發現了一篇有趣的博客文章 ,比較了字符串比較的所有方法。 可能不是很科學但仍然是一個很好的房子。

感謝這篇文章,我開始在一個場景中使用string.CompareOrdinal,我必須找出一個字符串是否在170.000個其他字符串的列表中並連續1600次執行此操作。 string.CompareOrdinal使它比string.Equals快了近50%

我使用秒表檢查了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都比僅比較更快..

這是我自己的觀察..你也可以嘗試在表單上放兩個按鈕並復制粘貼這個代碼在重新分級事件中。

這可能對某些人有用,但更改我的代碼的一行會使我的方法的單元測試從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。

我認為大多數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))

因此,您可以自由地切片和骰子,找到自己的做事方式。 請記住,雖然有人必須維護代碼,但可能不想花時間試圖弄清楚為什么要使用你決定使用的任何方法。

一如既往,優化自己的風險。 :-)

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM