• string中Insert与Format效率对比、String与List中Contains与IndexOf的效率对比


      关于string的效率,众所周知的恐怕是“+”和StringBuilder了,这些本文就不在赘述了。关于本文,请先回答以下问题(假设都是基于多次循环反复调用的情况下):
    1.使用Insert与Format方法,哪个效率更高?
    2.Contains(value)与IndexOf(value)谁效率更高?


    假如您对此2问不感兴趣或已非常了解,请忽略此文。另外本文将不对文中代码的实际用途做任何解释。

    <一> 首先看以下的使用场景

                string str1 = "abc";
                string str2 = "123";
                str1 = string.Format("{0}:{1}", str1, str2);
                str1 = str1.Insert(0, str2);

    接下来开始我们的对比之旅(不包含上述代码),编写如下代码用来向控制台输出结果

            static void WriteTime(string title, long time)
            {
                Console.WriteLine("{1}用时:{0} ms", time, title);
            }

    再添加一个方法对字符串进行循环操作

            static long LoopCalc(Action<string, string> action)
            {
                string[] array = new string[260000];            
                for (int i = 0; i < array.Length; i++)
                {
                    array[i] = i.ToString();
                }
    
                Stopwatch sw = new Stopwatch();
                sw.Start();
                for (int i = 0; i < array.Length; i++)
                {
                    action(array[i], "bc");
                }
    
                sw.Stop();
                return sw.ElapsedMilliseconds;
            }

    添加对string进行Insert与Format的效率对比代码并在Main方法中调用

            static void StringPlusDemo()
            {
                long inserTime = LoopCalc((x, y) => x.Insert(0, y));
                long formatTime = LoopCalc((x, y) => string.Format("{0}{1}", y, x));
                WriteTime("Insert", inserTime);
                WriteTime("Format", formatTime);
            }

    运行结果如下

    明显看到Insert效率更高,但是这种结果有局限性,如果字符串很长,那么经过我亲测他们效率相差无几。

    注:我这个只是将Format用于字符串拼接的场景,更高的效率应该仍然是StringBuilder,当然Format的其他不可替代用途太多了,Insert和StringBuilder根本无法替代它,这里就不罗嗦了。

    <二> 依然是先看下Contains与IndexOf的使用场景

                string str1 = "abcd";
                string str2 = "bc";
                if (str1.Contains(str2)) { }
                if (str1.IndexOf(str2) > -1) { }

    在这里仍然使用了上述的LoopCalc方法,并增加如下方法,然后在Main方法中调用其

            static void StringContainsDemo()
            {
                long indexOfTime = LoopCalc((x, y) => { if (x.IndexOf(y) >= 0) { } });
                long containersTime = LoopCalc((x, y) => { if (x.Contains(y)) { } });
                WriteTime("Contains", containersTime);
                WriteTime("IndexOf", indexOfTime);
            }

    结果

    显然Contains效率更高,为什么呢?我之前也不懂为什么,现在来看下String类的源码(关于.NET自带类库的源码可以谷歌搜到官方的下载地址,我忘了地址了),代码很多,我就贴出以下string类中的方法给各位看官

            // Determines the position within this string of the first occurence of the specified
            // string, according to the specified search criteria.  The search begins at
            // the first character of this string, it is case-sensitive and culture-sensitive, 
            // and the default culture is used.
            // 
            public int IndexOf(String value) { 
                return CultureInfo.CurrentCulture.CompareInfo.IndexOf(this,value);
            } 
    
            // Determines the position within this string of the first occurence of the specified
            // string, according to the specified search criteria.  The search begins at
            // startIndex, it is case-sensitive and culture-sensitve, and the default culture is used. 
            //
            public int IndexOf(String value, int startIndex) { 
                return CultureInfo.CurrentCulture.CompareInfo.IndexOf(this,value,startIndex); 
            }
            public bool Contains( string value ) {
                return ( IndexOf(value, StringComparison.Ordinal) >=0 );
            }

    以下是有关Contains调用的IndexOf的重载

            public int IndexOf(String value, StringComparison comparisonType) {
                return IndexOf(value, 0, this.Length, comparisonType); 
            }
    
            public int IndexOf(String value, int startIndex, StringComparison comparisonType) {
                return IndexOf(value, startIndex, this.Length - startIndex, comparisonType); 
            }
     
            public int IndexOf(String value, int startIndex, int count, StringComparison comparisonType) { 
                // Validate inputs
                if (value == null) 
                    throw new ArgumentNullException("value");
    
                if (startIndex < 0 || startIndex > this.Length)
                    throw new ArgumentOutOfRangeException("startIndex", Environment.GetResourceString("ArgumentOutOfRange_Index")); 
    
                if (count < 0 || startIndex > this.Length - count) 
                    throw new ArgumentOutOfRangeException("count",Environment.GetResourceString("ArgumentOutOfRange_Count")); 
    
     
                switch (comparisonType) {
                    case StringComparison.CurrentCulture:
                        return CultureInfo.CurrentCulture.CompareInfo.IndexOf(this, value, startIndex, count, CompareOptions.None);
     
                    case StringComparison.CurrentCultureIgnoreCase:
                        return CultureInfo.CurrentCulture.CompareInfo.IndexOf(this, value, startIndex, count, CompareOptions.IgnoreCase); 
     
                    case StringComparison.InvariantCulture:
                        return CultureInfo.InvariantCulture.CompareInfo.IndexOf(this, value, startIndex, count, CompareOptions.None); 
    
                    case StringComparison.InvariantCultureIgnoreCase:
                        return CultureInfo.InvariantCulture.CompareInfo.IndexOf(this, value, startIndex, count, CompareOptions.IgnoreCase);
     
                    case StringComparison.Ordinal:
                        return CultureInfo.InvariantCulture.CompareInfo.IndexOf(this, value, startIndex, count, CompareOptions.Ordinal); 
     
                    case StringComparison.OrdinalIgnoreCase:
                        return TextInfo.IndexOfStringOrdinalIgnoreCase(this, value, startIndex, count); 
    
                    default:
                        throw new ArgumentException(Environment.GetResourceString("NotSupported_StringComparison"), "comparisonType");
                } 
            }

    结论差不多出来了吧,不过这里还牵扯到另一个类CultureInfo.InvariantCulture.CompareInfo,我也看过该类的代码,里头有unsafe代码,不在本文范畴,但是有个结论就是当把我的Demo里的代码的IndexOf改为“x.IndexOf(y, StringComparison.Ordinal)”,那么他们俩的相率将相差无二。

    这里没有牵扯到正则匹配以及LastIndexOf,其实正则匹配有时可能效率比上述方式更高,但是要视场景使用,更通用的方式还是建议“IndexOf(value, StringComparison.Ordinal)”或“Contains”方法。

    string其本身就是char数组的封装,其或多或少体现着Array的一些特点,那么接下来再来看看在List集合中的关于Contains与IndexOf的情况。

    <三> List的IndexOf方法并没有StringComparison枚举作为参数的方法,直接上代码吧

    /// <summary>
            /// 演示string,在contain中还可延伸List类(实际上string就是char的集合)
            /// </summary>
            /// <param name="action"></param>
            /// <returns></returns>
            static long LoopCalcList(Action<List<int>, int> action)
            {
                List<int>[] array = new List<int>[260000];
                for (int i = 0; i < array.Length; i++)
                {
                    array[i] = new List<int>
                    {
                        i,1,2,3,4,5,6
                    };
                }
    
                Stopwatch sw = new Stopwatch();
                sw.Start();
                for (int i = 0; i < array.Length; i++)
                {
                    action(array[i], 3);
                }
    
                sw.Stop();
                return sw.ElapsedMilliseconds;
            }
    
            static void ListContainsDemo()
            {
                long indexOfTime = LoopCalcList((x, y) => { if (x.IndexOf(y) >= 0) { } });
                long containersTime = LoopCalcList((x, y) => { if (x.Contains(y)) { } });
                WriteTime("Contains", containersTime);
                WriteTime("IndexOf", indexOfTime);
            }
    List中的效率对比

    运行结果

    很显然在判断是否包含时,我们应该坚定不移的使用Contains。

    下面再给出List的源码,各位看官自行分析了,对此不做深入研究

            // Contains returns true if the specified element is in the List.
            // It does a linear, O(n) search.  Equality is determined by calling 
            // item.Equals().
            // 
            public bool Contains(T item) { 
                if ((Object) item == null) {
                    for(int i=0; i<_size; i++) 
                        if ((Object) _items[i] == null)
                            return true;
                    return false;
                } 
                else {
                    EqualityComparer<T> c = EqualityComparer<T>.Default; 
                    for(int i=0; i<_size; i++) { 
                        if (c.Equals(_items[i], item)) return true;
                    } 
                    return false;
                }
            }
    List类的Contains方法
            // Returns the index of the first occurrence of a given value in a range of
            // this list. The list is searched forwards from beginning to end. 
            // The elements of the list are compared to the given value using the
            // Object.Equals method. 
            // 
            // This method uses the Array.IndexOf method to perform the
            // search. 
            //
            public int IndexOf(T item) {
                return Array.IndexOf(_items, item, 0, _size);
            } 
    
            int System.Collections.IList.IndexOf(Object item) 
            { 
                if(IsCompatibleObject(item)) {
                    return IndexOf((T)item); 
                }
                return -1;
            }
     
            // Returns the index of the first occurrence of a given value in a range of
            // this list. The list is searched forwards, starting at index 
            // index and ending at count number of elements. The 
            // elements of the list are compared to the given value using the
            // Object.Equals method. 
            //
            // This method uses the Array.IndexOf method to perform the
            // search.
            // 
            public int IndexOf(T item, int index) {
                if (index > _size) 
                    ThrowHelper.ThrowArgumentOutOfRangeException(ExceptionArgument.index, ExceptionResource.ArgumentOutOfRange_Index); 
                return Array.IndexOf(_items, item, index, _size - index);
            } 
    
            // Returns the index of the first occurrence of a given value in a range of
            // this list. The list is searched forwards, starting at index
            // index and upto count number of elements. The 
            // elements of the list are compared to the given value using the
            // Object.Equals method. 
            // 
            // This method uses the Array.IndexOf method to perform the
            // search. 
            //
            public int IndexOf(T item, int index, int count) {
                if (index > _size)
                    ThrowHelper.ThrowArgumentOutOfRangeException(ExceptionArgument.index, ExceptionResource.ArgumentOutOfRange_Index); 
    
                if (count <0 || index > _size - count) ThrowHelper.ThrowArgumentOutOfRangeException(ExceptionArgument.count, ExceptionResource.ArgumentOutOfRange_Count); 
     
                return Array.IndexOf(_items, item, index, count);
            } 
    List类的IndexOf方法

    总结:

      1.这点效率问题对于某些人来说可能无所谓,但是我觉得更重要的是编码习惯的养成问题。

      2.能用Contains的地方还是尽量使用Contains(发现我改的代码中有不少同事直接用了"IndexOf(value)"),当然会有特殊的例外场景,这里不罗嗦。
      3.关于Insert,我编写了两个扩展方法,如下(方法虽简单,但是给代码带来了更大的优雅性)

            public static string InsertLast(this string source, string str)
            {
                return source.Insert(source.Length, str);
            }
    
            public static string InsertFirst(this string source, string str)
            {
                return source.Insert(0, str);
            }

     

    最后本人不是什么资深狼友,将不会提供任何福利图片作别,抱歉!!

  • 相关阅读:
    算法与数据结构基础
    算法与数据结构基础
    算法与数据结构基础
    算法与数据结构基础
    算法与数据结构基础
    算法与数据结构基础
    最佳实践 根据状态操作,这样能避免吃掉异常
    最佳实践 状态设计
    Android HTTPS如何10分钟实现自签名SSL证书
    马桶选购
  • 原文地址:https://www.cnblogs.com/FreeDong/p/3291673.html
Copyright © 2020-2023  润新知