Какое самое быстрое (встроенное) сравнение для строковых типов в C # - PullRequest
32 голосов
/ 21 сентября 2009

Какой самый быстрый встроенный метод сравнения для строковых типов в C #? Я не возражаю против типографского / семантического значения: цель состоит в том, чтобы использовать компаратор в отсортированных списках для быстрого поиска в больших коллекциях. Я думаю, что есть только два метода: Compare и CompareOrdinal. Какой самый быстрый?

Кроме того, есть ли более быстрый метод для сравнения строк?

Ответы [ 8 ]

57 голосов
/ 21 сентября 2009

Я предполагаю, что вам нужно сравнение меньше / равно / больше, чем просто равенство; Равенство - это немного другая тема, хотя принципы в основном одинаковы. Если вы на самом деле ищете только присутствие в чем-то вроде SortedList, я бы подумал об использовании Dictionary<string, XXX> - вам действительно нужна вся эта сортировка?

String.CompareOrdinal или с использованием перегрузки String.Compare, которая позволяет проводить сравнение, и с указанием порядкового (чувствительного к регистру) сравнения, например, String.Compare(x, y, StringComparison.Ordinal) будет самым быстрым.

По существу, порядковое сравнение просто необходимо пройти две строки, символ за символом, пока не обнаружит разницу. Если он не находит различий и длины одинаковы, результат равен 0. Если он не находит различий, но длины не совпадают, более длинная строка считается «большей». Если он обнаружит разницу, он может сразу определить, что считается «большим», исходя из того, какой символ «больше» в порядковых терминах.

Поместить это иначе: это все равно, что сделать очевидное сравнение между двумя char[] значениями.

Сравнения с учетом культурных особенностей должны выполнять все виды извилистых подвигов, в зависимости от конкретной культуры, которую вы используете. Для примера см. этот вопрос . Совершенно очевидно, что более сложные правила могут сделать это медленнее.

11 голосов
/ 02 сентября 2013

Я только что заметил, что производительность моего собственного кода увеличилась на 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
   }
}

Это может зависеть от ваших собственных строк, но, похоже, мне это помогло.

4 голосов
/ 09 августа 2012

Я разработал модульный тест для проверки скорости сравнения строк, используя некоторые из методов, упомянутых в этом посте. Этот тест был запущен с использованием .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 голосов
/ 21 сентября 2009

Fastest - это интернированные строки с тестом на равенство ссылок, но вы получаете только тест на равенство, и это требует больших затрат памяти - настолько дорого, что это почти никогда не рекомендуемый курс .

Кроме того, порядковый тест с учетом регистра будет самым быстрым, и этот метод абсолютно рекомендуется для не относящихся к культуре строк. Чувствителен к регистру быстрее, если он работает для вашего случая использования.

Если вы укажете StringComparison.Ordinal или StringComparison.OrdinalIgnoreCase, сравнение строк будет нелингвистическим. То есть особенности, характерные для естественного языка, игнорируются при принятии сравнительных решений. Это означает, что решения основаны на простых байтовых сравнениях и игнорируют таблицы соответствия или таблицы эквивалентности, которые параметризованы культурой. В результате, явно указав для параметра StringComparison.Ordinal или StringComparison.OrdinalIgnoreCase, ваш код часто набирает скорость , повышает правильность и становится более надежным.

Источник

2 голосов
/ 19 сентября 2017

Это довольно старый вопрос, но с тех пор, как я его нашел, другие тоже могут.

При более глубоком исследовании этой темы я наткнулся на интересное сообщение в блоге , в котором сравниваются все методы сравнения строк. Вероятно, не очень научный, но все же хороший номер.

Благодаря этой статье я начал использовать string.CompareOrdinal в сценарии, где мне нужно было выяснить, была ли одна строка в списке из 170 000 других строк, и делать это 1600 раз подряд. string.CompareOrdinal сделал это почти на 50% быстрее по сравнению со строкой. Equals

1 голос
/ 09 апреля 2014

Это может быть кому-то полезно, но изменение одной строки моего кода привело к снижению модульного тестирования моего метода со 140 мс до 1 мс!

Оригинал

Юнит-тест: 140 мс

public bool StringsMatch(string string1, string string2)
{
    if (string1 == null && string2 == null) return true;
    return string1.Equals(string2, StringComparison.Ordinal);
}

New

Юнит-тест: 1 мс

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 () был единственным модульным тестом, который занял 140 мс для метода StringsMatch. StringsMatch_AllParamsNullOrEmpty_ReturnTrue () всегда был 1 мс, а StringsMatch_OnlyString2NullOrEmpty_ReturnFalse () всегда <1 мс. </p>

1 голос
/ 21 сентября 2009

Я проверил и строку. Сравнение и строку. Сравнение обычного с использованием секундомера

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

Я пробовал с разными равными и не равными комбинациями строк и обнаружил, что каждый раз CompareOrdinal быстрее, чем только сравнение.

Это мое собственное наблюдение. Вы также можете попробовать просто поместить две кнопки в форму и скопировать и вставить этот код в событие regrading ..

0 голосов
/ 01 апреля 2018

Я думаю, что большинство разработчиков на 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 и HashSets, используя строки в качестве «ключей» и проверяя, существуют ли они уже со строкой, с которой вы хотите сравнить. Например: if (hs.Contains(stringsWeWantToSeeIfMatches))

Так что не стесняйтесь нарезать и нарезать кубиками, чтобы найти свои собственные способы ведения дел. Помните, что кто-то должен будет поддерживать код и, возможно, не захочет тратить время, пытаясь выяснить, почему вы используете тот метод, который вы решили использовать.

Как всегда, оптимизируйте как свой собственный риск. : -)

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...