【问题标题】:Is this the most efficient way to search for a substring?这是搜索子字符串的最有效方法吗?
【发布时间】:2011-11-02 03:27:14
【问题描述】:

我正在使用一些代码,它返回一个代码来指示他们是用户的类型(例如“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
}

这是执行此检查的有效方法吗?有没有更有效的方法?

提前致谢!

【问题讨论】:

    标签: c# performance


    【解决方案1】:

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

    string userCode = "B";
    string someStringToSearchIn = "Current user is: B";
    
    if (someStringToSearchIn.Contains(userCode))
    {
        //do something
    }
    

    【讨论】:

    • 这是倒退的。 userCode 是单个字母,因此您需要为每种可能的类型(“A”、“B”等)调用Contains()
    【解决方案2】:

    如果您要查找单个字符,并且它不区分大小写,请使用与 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

    【讨论】:

      【解决方案3】:

      查看Contains() 的反编译代码,它只是用StringComparison.Ordinal 调用IndexOf(),所以我会说IndexOf() 是最有效的(通过一个非常小的头发)我如果在相同的情况下使用方式(序数),因为它少了一个方法调用,但Contains() 更具可读性,因此更易于维护......

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

      与所有事情一样,我会选择更具可读性和可维护性的东西,然后在性能上分裂头发。仅当您知道此时存在瓶颈时才进行微优化。

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

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

      如您所见,非常非常接近。再一次,选择更易于维护的东西。

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

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

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

      这是因为Contains(char value)IEnumerable&lt;T&gt; 的扩展方法,而不是string 的第一类方法。

      但是再一次,超过 1,000,000 次迭代约 100 毫秒确实非常非常微不足道。

      【讨论】:

        【解决方案4】:

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

        【讨论】:

        • 我完全同意这个答案。很难想象性能在这里很重要,但如果是这样,位掩码将是一个好得多的解决方案。
        【解决方案5】:

        至少在我的电脑上,Containsstring(而不是 char)是最快的。

        结果:
        索引时间: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();
            }
        

        【讨论】:

        • 试试:IndexOf(value, StringComparison.Ordinal),我想你会发现它比 Contains 更快,因为 Contains 调用它。
        • @chibacity 也许它是 .NET 4.0 中的新功能?它肯定会编译并运行。我不知道这是否是投反对票的原因(或者是否是您),但如果是,我将不胜感激。
        • 啊,我明白了,它把字符串当作IEnumerable&lt;char&gt;,它只是IEnumerable&lt;T&gt;上的扩展方法
        【解决方案6】:

        首先,我不太确定您为什么在这里担心效率。您正在执行 非常 短字符串搜索,这不太可能导致性能下降。

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

        【讨论】:

        • 您对UserCode 枚举的实现很奇怪。标志位掩码枚举的意义在于,您不必定义ACBCABC 等。你为什么要那样做?
        猜你喜欢
        • 2012-12-18
        • 2014-03-23
        • 2019-01-21
        • 2021-08-20
        • 2012-11-15
        • 2010-10-10
        • 2013-08-22
        • 2014-04-02
        • 2012-03-23
        相关资源
        最近更新 更多