Это самый эффективный способ поиска подстроки? - PullRequest
4 голосов
/ 23 августа 2011

Я работаю с некоторым кодом, который возвращает код для указания типа пользователя (например, «A», «B», «C», «D» и т. Д.).Каждый код соответствует определенной роли и / или области действия (например, во всем приложении или только для объекта, над которым выполняется работа).

В некотором коде, на который я смотрю, я вижу вызовы, чтобы проверить,Код пользователя является одним из нескольких, чтобы позволить им выполнить какое-либо действие.Итак, я вижу такие вызовы:

//"B" would come from the database
string userCode = "B";

//some more work...

//if the user's code is either A or C...
if("AC".IndexOf(userCode) >= 0) {
  //do work that allows the user to progress
} else {
  //notify user they can't do this operation
}

Это эффективный способ выполнения этой проверки?Есть ли более эффективные способы?

Заранее спасибо!

Ответы [ 6 ]

5 голосов
/ 23 августа 2011

Глядя на декомпилированный код для Contains(), он просто вызывает IndexOf() с StringComparison.Ordinal, поэтому я бы сказал, что IndexOf() наиболее эффективен (очень маленьким волоском), если его использовать в том жеспособ (обычный), поскольку он имеет на один вызов меньше метода, но Contains() более читабелен и, следовательно, более удобен для обслуживания ...

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

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

ОБНОВЛЕНИЕ : более 1 000 000 итераций:

  • Содержит (значение) - заняло 130 мс
  • IndexOf (значение, StringComparison.Ordinal) - заняло 128 мс

Итак, как вы можете видеть, ОЧЕНЬ, ОЧЕНЬ ОДНО.Еще раз, перейдите к тому, что более легко обслуживаемо.

ОБНОВЛЕНИЕ 2 : Если ваш код всегда представляет собой один символ (а не строку из 1 символа), IndexOf () работает быстрее:

  • Содержит (значение символа) - заняло 94 мс
  • IndexOf (значение символа) - заняло 16 мс

Если вы знаете, что ваши коды символов - это всегда один символ, это примерно на порядок быстрее для использования IndexOf() с аргументом char.

Это потому, что Contains(char value) - это метод расширения из IEnumerable<T>, а не метод первого класса string.

Но еще раз ~ 100 мс на 1 000 000 итераций действительно, действительно, весьма незначительны.

3 голосов
/ 23 августа 2011

Я думаю, вы можете предположить, что системные библиотеки реализованы достаточно эффективно, и что вы, как правило, не можете ускорить процесс, используя домашние решения. Тем не менее, я думаю, что ваш способ кодирования пользовательских типов довольно странный. Почему бы не использовать битовые маски или что-то подобное? Кроме того, я бы предположил, что ваш вопрос вообще не имеет значения: CompareD для доступа к базе данных и выполнения «некоторой работы», ваша проверка вообще не имеет значения.

3 голосов
/ 23 августа 2011

Если вы ищете один символ, и он не чувствителен к регистру, используйте перегрузку, которая работает с символом.Поиск одного нечувствительного к регистру символа выполняется быстрее, чем подстрока.

"AC".IndexOf('C');

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

Обновление - Время

[Test]
public void Time()
{
    const string UserCode = "C";
    const char UserCodeChar = 'C';
    const int Iterations = 10000000;

    double adjust = 0;

    Func<Action, double> time = action =>
    {
        Stopwatch sw = Stopwatch.StartNew();
        for (int i = 0; i < Iterations; i++) action();
        return sw.Elapsed.TotalMilliseconds;
    };

    Action<string, Action> test = (desc, t) =>
    {
        double ms = time(t) - adjust;
        Console.WriteLine(desc + " time: {0}ms", ms);
    };

    adjust = time(() => { });

    test("IndexOfString", () => "AC".IndexOf(UserCode));
    test("IndexOfString", () => "AC".IndexOf(UserCode));

    test("ContainsString", () => "AC".Contains(UserCode));
    test("ContainsString", () => "AC".Contains(UserCode));

    test("IndexOfChar", () => "AC".IndexOf(UserCodeChar));
    test("IndexOfChar", () => "AC".IndexOf(UserCodeChar));
}

Результат:

IndexOfString time: 1035.2984мс
IndexOfString время: 1026.2889ms
ContainsString время: 764,9274ms
ContainsString время: 736.7621ms
IndexOfChar время: 92,9008ms
IndexOfChar время: 92,9961ms

* 1022
1 голос
/ 23 августа 2011

Использование функции Contains () является одним из вариантов. Я не знаю, как это работает по сравнению с индексом, но это вариант:

string userCode = "B";
string someStringToSearchIn = "Current user is: B";

if (someStringToSearchIn.Contains(userCode))
{
    //do something
}
0 голосов
/ 23 августа 2011

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

При этом я на самом деле нахожу стиль запутанным. Если вы хотите узнать, является ли userCode одним из "A" и "C", просто скажите так:

if (userCode.Equals("A") || userCode.Equals("C"))
{
    // Do something useful
}

Кристально чистый, и, скорее всего, также эффективный.

В качестве примечания: если вы можете вместо этого получить эти коды в enum, все может быть намного проще:

[Flags]
public enum UserCode
{
    None = 0,
    A = 1,
    B = 2,
    C = 4
}

Теперь вы можете просто сказать:

if ((userCode & (UserCode.A | UserCode.C)) != UserCode.None)
{
    // Do something even more useful
}
0 голосов
/ 23 августа 2011

По крайней мере, на моем компьютере, Contains с string (а не char) является самым быстрым.

Результаты:
Индекс времени: 616мс
Содержит время: 499 мс
Содержит время Char: 707ms

Код:

    static void Main(string[] args)
    {
        string userCode = "B";
        char userCodeChar = 'B';
        int iterations = 10000000;
        var sw = Stopwatch.StartNew();
        for (int i = 0; i < iterations; i++)
        {
            if ("AC".IndexOf(userCode) >= 0)
            {
                int a = 1 + 1;
            }
        }
        sw.Stop();
        Console.WriteLine("IndexOf time: {0}ms", sw.ElapsedMilliseconds);


        sw = Stopwatch.StartNew();
        for (int i = 0; i < iterations; i++)
        {
            if ("AC".Contains(userCode))
            {
                int a = 1 + 1;
            }
        }
        sw.Stop();
        Console.WriteLine("Contains time: {0}ms", sw.ElapsedMilliseconds);



        sw = Stopwatch.StartNew();
        for (int i = 0; i < iterations; i++)
        {
            if ("AC".Contains(userCodeChar))
            {
                int a = 1 + 1;
            }
        }
        sw.Stop();
        Console.WriteLine("Contains char time: {0}ms", sw.ElapsedMilliseconds);

        Console.WriteLine("Done");
        Console.ReadLine();
    }
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...