C#里string类型比StringBuilder类型的优势是什么?能举例说明吗?
String不可改变的,拼接效率低,因为会每次构造一个新的对象;StringBuilder是可以改变的,拼接采用追加模式,效率高。功能强大的自然消耗内存,功能简单的节省内存。
搞java的也可以扯一扯,相信很多人在面试或被面试的时候都遇到过这样的问题,“String 和 StringBuffer有什么区别,那个效率更高?”,一般性的回答以及网上很多面试题答案基本上是这样:“String对象内容是不可改变的,StringBuffer是可以改变的,并且高效。”, 这个结论似乎是一个经过很多人验证的,经验性的结论。但是事实真的如此么?为什么同样都是用来表示字符串,StringBuffer就会比String效率高呢? 为了验证这一广泛流传的"结论",我决定在各种情况下做几个实验来,检验一下到底在各种不同的场景下谁的效率更会高。
但首先在做实验之前,有必要科普一下关于字符串的基本常识。关于广为流传的“String对象内容是不可改变的”这句话主要是指String类被定义成final,而String类内用于存储字符串的字符数组,也被是被定义成final类型,这也就意味着,不管是String 对象,还是String 字面量,其最终的指针都是指向内存中的一个常量,所以他的内容是不可以修改的。同时从jvm虚拟层面来看当一个类中包含若干个字符串字面量,或者已经赋值的字符串对象,这些String对象或者String字面量所代表的字符串最终在编译的时候会被保存在.class文件的常量池中,而当jvm加载这些类的时候,对应的常量又会别加载到方法区的运行时常量池中,而这两个常量池的内容是1:1直接对应的。而关于String 还是StringBuffer那个效率更高,也主要是围绕这个常量池的存储装状态来进行的。
首先,我们知道,如果在java程序中我们直接写出一个字符串的内容,比如下面这行代码:
String Hello="Hello world";
上面这段斜体的蓝色字符串,我们在java中称之为字符串字面量,当在java程序中写出这些字面量时无论是在编译好的class文件还是当类别加载进内存后,其存在都是以常量的形式存贮在常量池中,而通常我们在代码开发规范之类的文档反对大量直接的使用字符串的原因也是因为,这样会在常量池中占用大量的单独的存储空间。那么如何让内存池中减少字符串的字面量存在的数量,也是我们在编码是极力追求的。下面就根据几种不同的情况对String,和StringBuffer 使用的不同场景进行对比和分析,来看一看那种情形下,效率更高。
首先为了对比试验,我写了如下代码:
public class StringBufferTest {
public StringBufferTest(){
StringBuffer sb =
new StringBuffer("第1501行 : 错误描述:用户名不存在第1502行 : 错误信息:取用户信息时错误");
sb.append("第1504行 : at cn.gwssi.app.login.LoginService.getUserInfo(Login");
sb.append(" : at cn.gwssi.app.login.LoginService.login(LoginService.java:7");
sb.append(".gwssi.app.login.LoginServiceService.__callService(Unknown Source");
sb.append("i.app.login.LoginServiceService.__doService(Unknown Source)第1508");
sb.append("txn.service.Step.process(Step.java:98)第1509行 : at cn.gwssi.comm");
System.out.print(sb.toString());
}
}
public class StringEfficientTest {
public StringEfficientTest() {
String testString =
"第1501行 : 错误描述:用户名不存在第1502行 : 错误信息:取用户信息时错误"
+ "第1504行 : at cn.gwssi.app.login.LoginService.getUserInfo(Login"
+ " : at cn.gwssi.app.login.LoginService.login(LoginService.java:7"
+ ".gwssi.app.login.LoginServiceService.__callService(Unknown Source"
+ "i.app.login.LoginServiceService.__doService(Unknown Source)第1508"
+ "txn.service.Step.process(Step.java:98)第1509行 : at cn.gwssi.comm";
System.out.print(testString);
}
}
如果不做实验单从我们的代码书写规范上来看,第一段代码似乎效率更好,不过到底谁效果更好我们需要查看这两个类所生成的class 文件来根据java常量池中的情况来判断。首先我们进入命令提示上,输入:
javap -verbose StringBufferTest >StringBufferTestRes.txt
javap -verbose StringEfficientTest >StringEfficientTestRes.txt
这两条命令,分别会生成两个txt文件,StringBufferTestRes.txt 和StringEfficientTestRes.txt分别代表两个类编译后的class文件结构,文件内包含若干部分,这里我们只关注Constant Pool部分,也就是常量池部分:
从上面两文件的Constant pool部分就可以看出来,同样情况下,StringBuffer版本的class文件中内存常量池里面常量的数量要比String版本的多出17个,而从其内容来看StringBuffer中的常量大部分都是程序中那些字符串字面量所对应的常量,而在String中本应该有同样多的字符串字面量,但实际上只用了一个字符串常量来表示所要输出的那些字符,也就是链接后的的字符串。这说明,在编译的时候java虚拟机的编译优化机制根据程序中字符串的连接方式,执行了优化,将本应该是单独的6个字符串字面量自动拼装成一个大的字符串。所以说,考虑到java虚拟机会执行编译优化,如果采用合适和字符串连接方式,那么字符串的效率要高于StringBuffer;
String对象内容是不可改变的,字符串拼接效率低,因为会每次构造一个新的对象;StringBuffer是可以改变的,字符串拼接采用追加模式,因此效率高。
string类型一个变量只能存储一个字符串,而StringBuilder实际上是一个可变的字符串数组,可以存储多个字符串。可以轻松实现数组到字符串的转化,也就是把数组中的字符串拼接成一个字符串。至于优势劣势你自己看吧,功能强大的自然消耗内存,功能简单的节省内存。
StringBuilder顾名思义是用来Build String的,所以没有StringBuilder我们还是可以自己去Build,但是没有String,那就啥也做不了了。
每种东西都有每种东西的用途,“优势”这种问法太简单粗暴了。你为什么活着,你活着有什么优势?