String.Contains () быстрее, чем String.IndexOf ()?


111

У меня есть строковый буфер размером около 2000 символов, и мне нужно проверить буфер, содержит ли он конкретную строку.
Выполняет проверку в веб-приложении ASP.NET 2.0 для каждого веб-запроса.

Кто-нибудь знает, работает ли метод String.Contains лучше, чем метод String.IndexOf ?

    // 2000 characters in s1, search token in s2
    string s1 = "Many characters. The quick brown fox jumps over the lazy dog"; 
    string s2 = "fox";
    bool b;
    b = s1.Contains(s2);
    int i;
    i = s1.IndexOf(s2);

Забавный факт


14
Если вам нужно сделать это миллиард раз на веб-запрос, я бы начал смотреть на такие вещи. В любом другом случае я бы не стал беспокоиться, поскольку время, потраченное на любой метод, скорее всего, будет невероятно незначительным по сравнению с получением HTTP-запроса в первую очередь.
mookid8000

2
Одним из ключей к оптимизации является тестирование, а не предположение, потому что это может зависеть от множества факторов, таких как версия .NET, операционная система, оборудование, вариации во входных данных и т. Д. Во многих случаях результаты тестов, выполненные другими может сильно отличаться в вашей системе.
Slai

Ответы:


174

Containsзвонки IndexOf:

public bool Contains(string value)
{
    return (this.IndexOf(value, StringComparison.Ordinal) >= 0);
}

Какие вызовы CompareInfo.IndexOf, которые в конечном итоге используют реализацию CLR.

Если вы хотите увидеть, как сравниваются строки в CLR, это покажет вам (ищите CaseInsensitiveCompHelper ).

IndexOf(string)не имеет параметров и Contains()использует порядковое сравнение (побайтовое сравнение вместо попытки интеллектуального сравнения, например, e с é).

Так IndexOf будет немного быстрее (теоретически), поскольку он IndexOfпереходит прямо к поиску строки с использованием FindNLSString из kernel32.dll (мощность отражателя!).

Обновлено для .NET 4.0 - IndexOf больше не использует порядковое сравнение, поэтому Contains может быть быстрее. См. Комментарий ниже.


3
Этот ответ далек от правильного, просто посмотрите здесь stackoverflow.com/posts/498880/revisions для объяснения
pzaj

55
Моему ответу 7 лет, и он основан на платформе .NET 2. Версия 4 IndexOf()действительно использует StringComparison.CurrentCultureи Contains()использует то, StringComparison.Ordinalчто будет быстрее. Но на самом деле разница в скорости, о которой мы говорим, незначительна - дело в том, что один вызывает другой, а Contains более читабелен, если вам не нужен индекс. Другими словами, не беспокойтесь об этом.
Chris S

21

Наверное, это вообще не будет иметь значения. Прочтите этот пост о Coding Horror;): http://www.codinghorror.com/blog/archives/001218.html


4
Подлизываемся к боссу, мы ...? : D Однако вы правы, по сравнению со временем, затраченным на обслуживание http-запроса, один раз поиск по короткой строке не имеет значения.
Fowl

Очень занимательное чтение, но меня беспокоит, что его первоначальная жалоба на конкатенацию - это использование памяти, а затем он проверяет только время, потраченное на различные способы комбинирования строк.
sab669

11

Contains (s2) во много раз (на моем компьютере в 10 раз) быстрее, чем IndexOf (s2), потому что Contains использует StringComparison.Ordinal, который быстрее, чем поиск с учетом языка и региональных параметров, который выполняет IndexOf по умолчанию (но это может измениться в .net 4.0 http: //davesbox.com/archive/2008/11/12/breaking-changes-to-the-string-class.aspx ).

Contains имеет точно такую ​​же производительность, что и IndexOf (s2, StringComparison.Ordinal)> = 0 в моих тестах, но он короче и проясняет ваше намерение.


2
Изменения в .NET 4.0, по-видимому, были отменены до того, как он стал RTM, поэтому я бы не стал слишком полагаться на эту статью blogs.msdn.com/bclteam/archive/2008/11/04/…
Стивен Кеннеди

7

Я использую реальный случай (в отличие от синтетического теста)

 if("=,<=,=>,<>,<,>,!=,==,".IndexOf(tmps)>=0) {

против

 if("=,<=,=>,<>,<,>,!=,==,".Contains(tmps)) {

Это жизненно важная часть моей системы, и она выполняется 131 953 раза (спасибо DotTrace).

Как ни шокирующе удивил , результат оказался противоположным ожидаемому.

  • Индекс 533 мс.
  • Содержит 266 мс.

: - /

net framework 4.0 (обновлено 13.02.2012)


1
потому что INTнамного больше, чем BOOL, и IndexOf>=0вызвать еще один шаг
Эрик Инь

3
Вы забыли использовать «StringComparison.Ordinal »
Дави Фиаменги

6

Используя Reflector, вы можете увидеть, что Contains реализуется с помощью IndexOf. Вот реализация.

public bool Contains(string value)
{
   return (this.IndexOf(value, StringComparison.Ordinal) >= 0);
}

So Contains, вероятно, немного медленнее, чем прямой вызов IndexOf, но я сомневаюсь, что это будет иметь какое-либо значение для реальной производительности.


1
Да, но чтобы использовать indexof как bool, ему пришлось бы проводить сравнение вне функции. Скорее всего, это даст тот же результат, что и Contains, не так ли?
Гонсало Куеро,

1
Возможно, но вы сохраняете один вызов метода (если он не может быть встроен). Как я уже сказал, это, вероятно, никогда не будет значительным.
Брайан Расмуссен

6

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

В инфраструктуре .net есть отличная реализация секундомера - System.Diagnostics.Stopwatch.


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

@JeremyThompson повторите метод «паузы отладки» примерно 10 раз, и вы получите профилировщик
Xeuron

4

Из небольшого чтения кажется, что под капотом метод String.Contains просто вызывает String.IndexOf. Разница в том, что String.Contains возвращает логическое значение, а String.IndexOf возвращает целое число с (-1), представляющим, что подстрока не была найдена.

Я бы посоветовал написать небольшой тест с примерно 100 000 итераций и убедиться в этом сам. Если бы я догадался, я бы сказал, что IndexOf может быть немного быстрее, но, как я уже сказал, это всего лишь предположение.

Джефф Этвуд опубликовал в своем блоге хорошую статью о струнах . Это больше о конкатенации, но, тем не менее, может быть полезно.


3

Так же, как обновление этого, я провел некоторое тестирование и предоставил вам довольно большую входную строку, тогда параллельное Regex - это самый быстрый метод C #, который я нашел (при условии, что у вас есть более одного ядра, как мне кажется)

Например, получение общего количества совпадений -

needles.AsParallel ( ).Sum ( l => Regex.IsMatch ( haystack , Regex.Escape ( l ) ) ? 1 : 0 );

Надеюсь это поможет!


1
Привет, phild в отдельном потоке обновил это версией с сайта tomasp.net/articles/ahocorasick.aspx, которая, если ваши ключевые слова (иглы) не меняются, намного быстрее.
Гэри

2

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

Пусть покупатель будет бдителен

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

Как и все вопросы (микро) производительности, первым шагом должно быть получение работающей версии, которую легко поддерживать. Затем к измеренным узким местам можно применить сравнительный анализ, профилирование и настройку вместо предположений.


Хотя эта ссылка может дать ответ на вопрос, лучше включить сюда основные части ответа и предоставить ссылку для справки. Ответы, содержащие только ссылки, могут стать недействительными, если ссылка на страницу изменится.
Майк Стокдейл

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

3
-1; вопрос был: «Кто-нибудь знает, работает ли метод String.Contains лучше, чем метод String.IndexOf?» - ваш ответ - «используйте библиотеку тестов», что в основном означает «я не знаю, сделай сам», «это зависит от обстоятельств», что означает «я не знаю» и «получить работающую версию и профиль». , что также означает «Я не знаю, сделай сам». Это не «Опасность» - пожалуйста, ответьте на заданный вопрос , а не практические идеи - их место в комментариях .

-7

Для тех, кто все еще читает это, indexOf (), вероятно, будет лучше работать в большинстве корпоративных систем, поскольку contains () несовместим с IE!


12
выбросить новое исключение OutOfScopeException ();
Raphaël
Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.