《软件开发性能优化系列》之String操作- 阿军- 博客园

      string是不变类,使用+操作连接字符串会导致创建一个新的字符串。如果字符串连接次数不是固定的,例如在一个循环操作中,则应该使用StringBuilder类来做字符串连接工作。因为StringBuilder内部有一个StringBuffer,连接字符操作不会每次分配新的字符串空间。只有当连接后的字符串超出Buffer大小是,才会申请信的Buffer空间。典型代码如下:

StringBuiler sb = new StringBuilder(256);

for(int i = 0; i < str.Count; i++)

{

      sb.Append(str[i]);

}

      而如果连接字符数十固定的并且只有几次,此时应该直接用+号连接,保持程序简洁易读。实际上,编译器已经做了优化,会依据加号次数调用不同参数个数的String.Concat方法。例如:

      String str =  str1 + str2 + str3 + str4;

      会被编译成:Sting.Concat(str1,str2,str3,str4).该方法内部会计算总的String长度,仅分配一次,并不会如通常想象的那样分配三次。作为一个值,当字符串连接操作达到10此以上时,则应该使用StringBuilder.

这里有个细节要注意:StringBuilder内部Buffer的缺省值为16,这个实在太小。按照StingBuilder的使用场景,Buffer肯定得重新分配。我建议使用256作为Buffer的初值。当然,如果能计算出最终生成字符串长度的话,则应该按这个值来设定Buffer的初值。我曾经开发过一个44位的UUID生成方法,仅仅把new StringBuilder()改为StringBuilder(44)前后就有3倍的效率差异。

     String是不变类,调用ToUpper或ToLower方法都会导致创建一个新字符串。如果被频繁调用,将导致频繁创建字符串对象。这违背了前面讲到的“避免频繁创建对象”这一基本原则。

     例如,bool.Parse方法本身已经是忽略大小写的,但下面的代码每次访问IsNullable属性时,都要不必要的调用ToLower方法:

      另外一个非常铺平的场景是字符串比较,例如:

        foreach (XmlNode node in DocumentElement.ChildNodes)
        {
            if (node is XmlElement)
            {
                if (node.Name.ToLower() == "appender")
                {
                    Respoitory.AppenderLoader.Load(node);
                }
                else if (node.Name.ToLower() == "Render")
                {
                    Respoitory.AppenderLoader.Load(node);
                }
                else if (node.Name.ToLower() == "Render")
                {
                    Respoitory.RegisterUtilRunner.RunningRegisterUtil(node);
                }
            }
        }

 

      高效的做法是使用Compare方法,这个方法可以做大小写忽略的比较,并且不会创建新字符串:

      if(String.Compare(node.Name,"appender",StringComparison.OrdinalIgnoreCase)==0)

      {zh1}列举的一个示例是使用HashTable的时候,有时候无法保证传递key的大小写是否符合预期,往往会把key强制转换到大小写方式,例如:myTalbe.Add(myKey.ToLower(),myObject).实际上HashTable有不同的构造方式,xx支持采用忽略大小写的Key:new HashTable(StringComparer.OrdinalIgnoreCase).

     将String对象的Length属性与0比较式最快的方法:if(str.Length == 0)

      其次是与sting.Empty常量或空串比较;if(str == String.Empty)或if(str == "")

      注:C#在编译时会将程序集中声明的所有字符串常量放到保留池中(intern pool),相同常量不会重复分配。


吉日嘎拉&gt;不仅权限设计
你说错了。在编码规范上,基本类型(有c#关键字的),声明时要用关键字,比如int i = 0, string s = String.Empty
而在调用其方法时候,要用大写的类名。比如String.IsNullOrEmpty(s)

为什么呢?因为使用关键字方法string.IsNullOrEmpty(s),会很奇怪,让人感到怎么关键字也有方法的?和我们一般理解的调用 类.静态方法 有出入。

军军
规范那回复不是说给你听的,是反驳老吉的。
至于文章内容
1:我不清楚为何不用String.IsNullOrEmpty()方法,而要用String.Length==0?
2:Campare虽然高效,但其函数的意义和Equal还是有区别的。从你的这个方法来看,目的是判断是否相等,而不是判断2个字符串谁大谁小,因此用bool Equals(string value,StringComparison comparisonType)比较合适。如果你非讲究效率,在区分大小写的情况下,String.CompareOrdinal()比Campare()快3-4倍。
3:String str = str1 + str2 + str3 + str4; 分配一次,这个是有前提条件的。条件就是strxxx是静态值,也就是已经写好的字符串,比如 string str = "A"+ "B" + "C",这样编译时候str="ABC"。
而在下面语句中,由于str1,str2在编译时不确定,因此str的“=”操作可不是执行一次,而是三次。
public string Output(string str1, string str2, string str3, string str4)
{
    string str = str1 + str2 + str3 + str4;
    return str;
}

所以我说那个算术表达式是有条件的。你的那句只在常量条件下适用。

晕。。。楼上跟我说的差不多,我慢了

但对于楼上的第三点,显然跟楼主的意思不同。
事实上,用+的时候,如果+号两边都是常量,如:
string str = "a"+"b";
则编译后会是string str = "ab";
如果+号其中一边不是常量,则会编译成string.Concat方法的调用,这个是楼主想表达的,如:
string str = str1 + str2 + str3; // 假设3个都不是常量
编译后会是:
string str = String.Concat(str1,str2,str3)
而若写成:
string str = str1 + str2;
string str += str3;
则会出现str1+str2产生的中间对象,是不必要的,这种写法不被推荐

Prime Li
首先非常感谢您的探讨,能一起探讨,我非常高兴。
1、使用String.Length==0的条件是知道string不为null的条件
我做过一个测试:
a)、str==""
b)、str==string.Empty
c)、string.IsNullOrEmpty(str)
d)、str.Length == 0
效率是依次递增的
2、首先,您的建议非常好,但是我不知道String.CompareOrdinal()比Campare()快3-4倍您有没有做过测试。我有时间的话,测试一下,不是怀疑你,只是实践是检验真理的{zh0}方法
3、我同意你的看法

郑重声明:资讯 【《软件开发性能优化系列》之String操作- 阿军- 博客园】由 发布,版权归原作者及其所在单位,其原创性以及文中陈述文字和内容未经(企业库qiyeku.com)证实,请读者仅作参考,并请自行核实相关内容。若本文有侵犯到您的版权, 请你提供相关证明及申请并与我们联系(qiyeku # qq.com)或【在线投诉】,我们审核后将会尽快处理。
—— 相关资讯 ——