这是搜索子字符串的最有效方法吗?

时间:2011-08-23 16:59:41

标签: c# performance

我正在处理一些代码,这些代码返回一个代码来指示它们的用户类型(例如“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 个答案:

答案 0 :(得分:5)

查看Contains()的解压缩代码,只需使用IndexOf()调用StringComparison.Ordinal,因此我认为IndexOf()效率最高(非常小)我是否以相同的方式使用(Ordinal),因为它只有少一个方法调用,但Contains()更具可读性,因此更易于维护......

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

就像在所有事情中一样,我会选择更具可读性和可维护性的东西,然后分解性能。当你知道此时存在瓶颈时,才进行微优化。

更新:超过1,000,000次迭代:

  • 包含(值) - 耗时130毫秒
  • IndexOf(value,StringComparison.Ordinal) - 耗时128毫秒

所以你可以看到,非常非常接近。再一次,用更可维护的东西。

UPDATE 2 :如果您的代码始终是单个字符(不是1-char字符串),则IndexOf()更快:

  • 包含(字符值) - 耗时94毫秒
  • IndexOf(字符值) - 耗时16毫秒

如果您知道您的char代码始终是一个char,那么将IndexOf()与char参数一起使用会快一个数量级。

这是因为Contains(char value)IEnumerable<T>之外的扩展方法,而不是string的第一类方法。

再次在100,000次迭代中再次~100 ms真的,真的,可以忽略不计。

答案 1 :(得分:3)

如果您正在寻找单个字符,并且它不区分大小写,请使用与char一起使用的重载。搜索单个不区分大小写的char比子字符串更快。

"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时间:1035.2984ms
  IndexOfString时间:1026.2889ms
  包含字符串时间:764.9274ms
  包含字符串时间:736.7621ms
  IndexOfChar时间:92.9008ms
  IndexOfChar时间:92.9961ms

答案 2 :(得分:3)

我认为您可以假设系统库的实现非常高效,并且您通常无法使用自制解决方案加快速度。也就是说,我认为你对用户类型进行编码的方式很奇怪。为什么不使用Bitmasks或类似的东西?除此之外,我认为你的问题根本不相关:比较D来访问数据库并做“一些工作”你的支票根本不重要。

答案 3 :(得分:1)

使用Contains()函数是一个选项。我不知道它与索引的表现如何,但它是一个选项:

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

if (someStringToSearchIn.Contains(userCode))
{
    //do something
}

答案 4 :(得分:0)

首先,我不确定你为何关注这里的效率。您正在执行短字符串搜索,这不太可能降低性能。

话虽如此,我实际上发现这种风格令人困惑。如果你想知道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
}

答案 5 :(得分:-1)

至少在我的计算机上,Contains string(而不是char)是最快的。

结果:
IndexOf时间:616ms
包含时间:499ms
包含字符时间: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();
    }