一、分析
String类字符串追加
当我们对字符串进行拼接时,String:是对象不是原始类型.为不可变对象,一旦被创建,就不能修改它的值.对于已经存在的String对象的修改都是重新创建一个新的对象,然后把新的值保存进去.String 是sealed 类,即不能被继承.
string是String类的别名,引用类型,且该类型是只读的,不可修改,当你修改字符串内容时,实际上是创建了另一个新的字符串并将引用指向了它,你可以参考String类型的构造函数说明,就明白错在哪里了。第一条只是创建了一个String类型的数组,并未对其初始化,如果你对该数组中的任一成员使用第二条的方法进行初始化,一样会报错。
str += "a";等效于:str = new StringBuffer(str).append("a").toString(); 虽然编译器对字符串加号做了优化,它会用StringBuffer的append方法进行追加。再是通过toString方法转换成String字符串的。 它与纯粹的append方法是不同的:
一是每次都要创建一个StringBuilder对象;
二是每次执行完毕都要调用toString方法将其转换为字符串。
因此,字符串类型的字符追加有可能会消耗大量的时间,因为每次追加完成之后都要return返回一个新的字符串,每次的操作都会创建一个新的String对象,这就是直接使用速度慢下来的原因。
StringBuffer的字符串追加
StringBuffer:是一个可变对象,当对他进行修改的时候不会像String那样重新建立对象,它只能通过构造函数来建立,StringBuffer sb = new StringBuffer();它不能直接对其进行赋值操作,sb = "I Hava a Dream";//错误,对象被建立以后,在内存中就会分配内存空间,并初始保存一个null.向StringBuffer中赋值的时候可以通过它的append方法.sb.append("当然的");
StringBuilder
-> 字符串不可变,长期拼接字符串性能较低
-> Stringbuilder
Append(string);
AppendLine(string);
AppendFormat("{0}{1}{2}{3}", 1, "23", "ab", true);
ToString();
所以它必须通过追加的方式进行赋值。它的内部实现代码如下:
1 public AbstractStringBuilder append(String str){ 2 //如果是null值,则把null作为字符串处理 3 if(str == null)str = "null"; 4 5 int len = str.length(); 6 //字符串的长度为0,则返回自身 7 if(len == 0)return this; 8 9 int newCount = count + len; 10 //追加后的字符串组长度是否超过当前值 11 if(newCount > value.length) 12 expandCapacity(newCount);//加长,并作数组拷贝 13 //字符串复制到目标数组 14 str.getChars(0, len, value, count); 15 count = newCount; 16 17 return this; 18 }
在StringBuffer中进行追加时不需要建立新的对象,开辟新的空间,它始终是一个对象,而String 对象是不可变对象,每次操作Sting 都会重新建立新的对象来保存新的值.
这样原来的对象就没用了,就要被垃圾回收.这也是要影响性能的。
二、场景模拟
我们使用Stopwatch类分别对这两种情况进行模拟计时,在C#中有一个秒表类:stopwatch,用这个类可以方便的测试一下代码运行时间。
1 Stopwatch sp=new Stopwatch(); 2 sp.start(); 3 string s=""; 4 for(int i=0;i<100000;i++) 5 { 6 s+=i.toString(); 7 } 8 sp.Stop(); 9 Console.WriteLine(sp.Elapsed); 10 Console.ReadKey();
结果表明:时间花了30多秒
而对于StringBuilder:
1 Stopwatch sp=new Stopwatch(); 2 sp.start(); 3 StringBuilder sb=new StringBuilder(); 4 for(int i=0;i<100000;i++) 5 { 6 s+=sb.Append(i.toString()); 7 } 8 sp.Stop(); 9 Console.WriteLine(sp.Elapsed); 10 Console.ReadKey();
结果表明:时间花了0.几秒,相差了两个数量级
三、结论
当我们的系统性能不临界的时候,string类型的简答追加比较接地气,符合人们通常的写代码的习惯,而当系统性能临界,我们要进行数量级比较大的循环,我们就应该使用StringBuilder,提高我们的代码优化。