xingd.net

.net related techonology
posts - 56, comments - 622, trackbacks - 6, articles - 0

String与StringBuilder

Posted on 2005-02-05 01:55 xingd 阅读(4941) 评论(6) 编辑 收藏
  原文见http://www.cnblogs.com/birdshome/archive/2005/02/05/101075.html

  文中对String和StringBuilder的用法有一些偏差,首先,str = str.SubString(0, str.Length - 1)和str = str.SubString(1)的效率应该相差不大,String.SubString(int)的实现如下:

public string Substring(int startIndex)
{
      
return this.Substring(startIndex, this.Length - startIndex);
}

  因为SubString的两个重载如果需要得到相同长度的子串,分配内存和拷贝数据的时间消耗应当是差不多的。

  StringBuilder的ToString方法提供了一个重载,如下:
public string ToString(int startIndex, int length)
{
      
return this.m_StringValue.Substring(startIndex, length);
}

  m_StringValue是一个string的实例,用以维持StringBuilder内部的值。因为SubString只涉及到内存分配和拷贝,而设置StringBuilder的Length时,需要更新StringBuilder的状态,并且最终使用时仍需要一次ToString()操作,在set_Length和ToString()时都可能会产生新的string实例,因此两种用法效率上的差别是无法直观判断的,需要通过测试来确定。现在的项目中我使用的是设置Length的方式,但有意图要测试一下ToString()的情况,以确定是否需要修改。

  StringBuilder是一个expensive的对象,构建时代价比较大,因此除非有大量的或是无法预知次数的字符串操作,否则可以用string的操作来代替。原文中的几个功能都可以用string类的方法来实现。

  首先是实现包含n个空格的字符串,有如下两种方式,效率都比较高:
string str1 = String.Empty.PadRight(10);
string str2 = new String(' '10);

  除了空格外,PadRight也可以指定其他的填充字符。

  要生成类似于"a,b,c,d,e,f"这样的字符串,可以用String类的静态方法String.Join,代码如下:
string str = String.Join(","new string[]{"a""b""c""d""e"});

  StringBuilder可以用来进行大量的字符串操作,但是构建StringBuilder的代码比较大,因此对于简单的字符串连接操作,可以使用String.Concat(params string[])方法。实际上,C#的编译器会为字符串的operator +生成调用String.Concat(params string[])的中间代码。编译器也为多个字符串的连接做了优化,例如以下四个字符串相连接,仅生成一次String.Concat(params string[])调用,而不是三次字符串连接。


string str1 = "hello";
string str2 = "world";

string str = str1 + " " + str2 + "!";


  String.Concat会分析传入的多个参数,一次性分配结果所需的内存,并将参数值依次拷贝到结果字符串的相应部分,效率还是相当高的。

  那么,以下的代码会生成对哪个方法的调用呢?

string str1 = "hello";
string str2 = "world";
int i = 2;

string str = str1 + " " + i + str2 + "!";

  因为i是Int32,因此根据Overload的规则,编译器最终会产生对String.Concat(params object[])的调用,这个方法的执行过程与String.Concat(params string[])类似,但会为每一个参数调用ToString()操作,即便此参数是string类的实例,因此,为了减少这一开销,应当如下编写:

string str1 = "hello";
string str2 = "world";
int i = 2;

string str = str1 + " " + i.ToString() + str2 + "!";

  最后,要说明的是string.PadRight及string.Join,还有string的构造函数,其最终依赖的实现方法声明都类似于:
[MethodImpl(MethodImplOptions.InternalCall)]
public static extern string Join(string separator, string[] value, int startIndex, int count);
  
  这类代码都是内部实现的,效率非常高。

Feedback

#1楼  回复 引用   

2005-02-05 09:39 by KingofSC
stringbuilder顾名思义是用来build string的
设计目的就是针对大量string的操作,避免产生太多的临时对象
至于构建代价大,不知道作者的意思是否new一个stringbuilder的时候会一次分配很大的内存?
希望作者给出更详尽的数字说明,以便我们在使用stringbuilder的时候更真实一些。
比如如果stringbuilder一次分配1k,那么就算我有很多的针对很小的string的操作是否值得用stringbuilder。又或者stringbuilder有参数设置分配长度?
哈哈,或者我应该去看一下msdn就明了。
不过我只是想借助一下作者的研究成果,就省事多了, :)

#2楼[楼主]  回复 引用 查看   

2005-02-05 10:17 by xingd      
StringBuilder默认分配16个字符长度,也可以在构造函数中指定。
在每次Append的过程中,如果保留的内存不足,需要另外分配一个2倍长度的字符串,同时还需要维持length和capacity等状态,因此如果是简单的字符串连接,使用String.Concat还是快得多。

这只是原理上的分析,我也没有做过测试。如果有朋友认为我分析的有问题,我就做一下测试。

#3楼  回复 引用   

2005-02-05 10:26 by birdshome
very good!

关于那个什么
str = str.Substring(0, str.Length-1);和
str = str.Substring(1)
我完全没有考虑其效率,只是觉得第二个语句超级简练:)

String居然可以 new String(" ", 10),绝倒@_@!

#4楼  回复 引用   

2005-02-05 10:29 by birdshome
要生成类似于"a,b,c,d,e,f"这样的字符串的问题,我想的是做很多需要从循环里构建的情况,那样会多一个separator出来,我建议如果用string就把separator放开头,也是为了截断时代码简单。

#5楼  回复 引用 查看   

2007-01-23 09:44 by 念时      
当程序很大时,效率就变得很重要了

#6楼  回复 引用 查看   

2010-06-17 22:18 by codefor      
stringbuilder重置length后是不是那块内存就释放了还是还留在那里啊?