Чи 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, операційна система, апаратне забезпечення, зміна вхідних даних тощо. У багатьох випадках результати тестів, зроблені іншими можуть бути дуже різними у вашій системі.
Слай

Відповіді:


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 більше не використовує звичайного порівняння, і тому вміст може бути швидшим. Дивіться коментар нижче.


3
Ця відповідь ніде не є правильною, просто подивіться тут stackoverflow.com/posts/498880/reitions для пояснення
pzaj

55
Моїй відповіді 7 років і ґрунтуються на .NET 2. Версія 4 IndexOf()дійсно використовує StringComparison.CurrentCultureта Contains()використовує, StringComparison.Ordinalщо буде швидше. Але насправді різниці швидкостей, про які ми говоримо, хвилини - справа в тому, що один дзвонить іншому, а "Містить" - читабельніше, якщо вам не потрібен індекс. Іншими словами, не хвилюйтеся з цього приводу.
Кріс S

21

Напевно, це взагалі не матиме значення. Прочитайте цю публікацію на темі Кодування жахів;): http://www.codinghorror.com/blog/archives/001218.html


4
Ми до боса присмоктуємось? : D Ви маєте рацію, хоча порівняно з часом, необхідним для обслуговування запиту http, пошук одного разу по короткому рядку не має великого значення.
Птиця

Дуже цікаве читання, але мене турбує його початкова скарга на конкатенацію - це використання пам'яті, то він лише перевіряє час, витрачений на різні способи поєднання рядків.
sab669

11

Містить (s2) у багато разів (на моєму комп’ютері в 10 разів) швидше, ніж IndexOf (s2), тому що Contains використовує StringComppare.Ordinal, що швидше, ніж пошук, орієнтований на культуру, який за замовчуванням робить IndexOf (але це може змінитися в .net 4.0 http: //davesbox.com/archive/2008/11/12/breaking-changes-to-the-string-class.aspx ).

Містить має точно таку ж ефективність, що і IndexOf (s2, StringComppare.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).

Як би шокуюча несподіванка , результат протилежний очікуваному

  • IndexOf 533ms.
  • Містить 266 мс.

: - /

net Framework 4.0 (оновлено 13-02-2012)


1
тому що INTнабагато більший за BOOLта ще IndexOf>=0один крок
Ерік Інь

3
Ви забули скористатися ´StringComppare.Ordinal´
Davi Fiamenghi

6

Використовуючи Reflector, ви можете бачити, що Contains реалізується за допомогою IndexOf. Ось реалізація.

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

Отже, Contains, швидше за все, трохи повільніше, ніж викликати IndexOf безпосередньо, але я сумніваюся, що це матиме якесь значення для фактичної продуктивності.


1
Так, але щоб використовувати indexof як bool, йому доведеться робити порівняння поза функцією. Це, швидше за все, дасть той самий результат, що і Містить, чи не так?
Гонсало Керо

1
Можливо, але ви зберігаєте один виклик методу (якщо він не може бути вписаний). Як я вже сказав, це, мабуть, ніколи не буде суттєвим.
Брайан Расмуссен

6

Якщо ви дійсно хочете мікро-оптимізувати свій код, ваш найкращий підхід - це завжди тестування.

Рамка .net має чудову реалізацію секундоміра - System.Diagnostics.Stopwatch


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

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
Привіт філді в окремому потоці оновив це версією від tomasp.net/articles/ahocorasick.aspx, яка, надаючи ваші ключові слова (голки), не змінюється набагато швидше.
гарі

2

Використовуйте бібліотеку-орієнтир, як цей останній набір від Джона Скіта, щоб виміряти його.

Caveat Emptor

Як і всі (мікро-) питання щодо продуктивності, це залежить від версій програмного забезпечення, яке ви використовуєте, деталей перевірених даних та коду, що оточує виклик.

Як і всі (мікро-) питання щодо продуктивності, першим кроком має стати отримання запущеної версії, яка легко досяжна. Тоді до вимірюваних вузьких місць можна застосувати тестування, профілювання та налаштування замість здогадок.


Хоча це посилання може відповісти на питання, краще включити сюди суттєві частини відповіді та надати посилання для довідки. Відповіді лише на посилання можуть стати недійсними, якщо пов’язана сторінка зміниться.
Майк Стокдейл

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

3
-1; питання було "Хто-небудь знає, чи метод String.Contains працює краще, ніж метод String.IndexOf?" - Ваша відповідь - "використовувати бібліотеку еталонів", що в основному означає "я не знаю, зробіть це самі", "це залежить", що означає "я не знаю", і "отримати працюючу версію та профіль" , що також означає "я не знаю, зробі сам". Це не «небезпека» - будь ласка, надайте відповідь на поставлене запитання , а не ідеї , як їх ідеї - їх місце в коментарях .

-7

Для тих, хто все ще читає це, indexOf (), ймовірно, буде працювати в більшості корпоративних систем, оскільки містить () не сумісний з IE!


12
кинути новий OutOfScopeException ();
Рафаель
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.