一个String对象的长度是固定的,不能改变它的内容,或者是附加新的字符至String对象中。您也许会使用+来串联字符串以达到附加新字符或字符串的目的,但+会产生一个新的String实例。如果程序对这种附加字符串的需求很频繁,并不建议使用+来进行字符串的串联。在面向对象程序设计中,最好是能重复运用已生成的对象,对象的生成需要内存空间与时间,不断地产生String实例是一个没有效率的行为。
J2SE 5.0提供java.lang.StringBuilder类,使用这个类所产生的对象默认会有16个字符的长度,您也可以自行指定初始长度。如果附加的字符超出可容纳的长度,则StringBuilder对象会自动增加长度以容纳被附加的字符。如果有频繁作字符串附加的需求,使用StringBuilder会让程序的效率大大提高。通过下面的简单测试程序就可以知道效能差距有多大。
范例
在范例中首先使用+来串联字符串,使用System.currentTimeMillis()取得for循环执行前、后的系统时间,这样就可以得知for循环执行了多久。以下是我的计算机上的测试数据:
执行时间:4641
执行时间:16
可以看到执行的时间差距很大,这说明了使用+串联字符串所带来的负担。如果有经常作附加字符串的需求,建议使用StringBuilder。事实上就范例6.5来说,第二个for循环执行时间还可以更短,因为append()也可以接受基本数据类型,所以不必特地使用String.valueOf()方法从int取得String。改为以下的方式,执行时间可以大幅缩短:
for(int i = 0; i < 10000; i++)
builder.append(i);
使用StringBuilder最后若要输出字符串结果,可以用toString()方法。可以使用length()方法得知目前对象中的字符长度,而capacity()可返回该对象目前可容纳的字符容量。另外,StringBuilder还有像insert()方法可以将字符插入指定的位置,如果该位置以后有字符,则将所有的字符往后移;deleteChar()方法可以删除指定位置的字符,而reserve()方法可以反转字符串。详细的使用可以查询java.lang.StringBuilder的API文件说明。
StringBuilder是J2SE 5.0才新增的类,在J2SE 5.0之前的版本若有相同的需求,则使用java.lang.StringBuffer。事实上,StringBuilder被设计为与StringBuffer具有相同的操作接口。在单机非多线程(Multithread)的情况下使用StringBuilder会有较好的效率,因为StringBuilder没有处理同步(Synchronized)问题。StringBuffer则会处理同步问题,如果StringBuilder会在多线程下被操作,则要改用StringBuffer,让对象自行管理同步问题。
StringBuilder 是从 Java 5 以后增加的一个字符串处理类。查看 API 文档,我们可以知道 StringBuilder 和 StringBuffer 提供同样的功能,只是 StringBuilder 不保证线程安全,所以性能比 StirngBuffer 好,并推荐在确定线程安全的情况下,尽量用 StringBuilder 。事实真是如此吗?让我们通过一个小试验来看看
试验设计:
分别用 StringBuilder 和 StringBuffer 将一指定的字符串自连接一百万次,比较两种方法所用的时间。为尽量避免环境的干扰,测试时会关闭本机中其它应用程序,并且为了避免测试组之间的相互干扰,在每组测试完成后会重起机器。每个程序运行十次,最后取平均值。
测试环境:CPU: Celeron – M420
RAM: 1G
OS: Window XP Home Edition
JDK: Sun JDK 1.6.0 (Java HotSpot™ Client VM (build 1.6.0-b105, mixed mode, sharing))
运行程序时没有为 JVM 指定任何参数,全部使用默认值
程序段:
1. 用 StringBuffer
2. 用 StringBuilder
private static final int COUNT = 1000000;
测试结果:
No. |
StringBuffer |
StringBuilder |
1 |
328 |
328 |
2 |
344 |
312 |
3 |
328 |
328 |
4 |
344 |
312 |
5 |
344 |
328 |
6 |
344 |
312 |
7 |
328 |
328 |
8 |
344 |
312 |
9 |
343 |
328 |
10 |
344 |
328 |
平均值 |
339.1 |
321.6 |
从结果中可以看出两者的性能差异约为 5.44 %
下面我们将对测试程序做一点点小小的改动,在 new 一个新的 StringBuffer/StringBuilder 时,我们指定一个容量参数。修改的代码如下:
1. 用 StringBuffer
2. 用 StringBuilder
测试结果:(表格中第一,二组为上一轮测试的结果)
No. |
StringBuffer |
StringBuilder |
StringBuffer(int) |
StringBuilder(int) |
1 |
328 |
328 |
140 |
94 |
2 |
344 |
312 |
125 |
125 |
3 |
328 |
328 |
125 |
93 |
4 |
344 |
312 |
125 |
125 |
5 |
344 |
328 |
109 |
94 |
6 |
344 |
312 |
125 |
110 |
7 |
328 |
328 |
125 |
110 |
8 |
344 |
312 |
110 |
110 |
9 |
343 |
328 |
140 |
109 |
10 |
344 |
328 |
109 |
125 |
平均值 |
339.1 |
321.6 |
123.3 |
109.5 |
从表中可以看到 StringBuffer(int) 和 StringBuilder(int) 两者之间的差异为 12.6% 。但我们更应该看到采用不同的构造方法所带来的性能提升, StringBuffer 提升了 175.02 %, StringBuilder 提升了 193.70% 。原因在于不指定 StirngBuffer/StringBuilder 的容量时,它们内部的字符缓冲区为 16 个字符(无参构造)或字符串参数的长度,当程序不断的进行 append/insert 操作时,每当字符数超过原有的容量后,
StringBuffer/StringBuilder 将不断的进行自动扩展的工作,这将消耗比较多的时间。
也许有人会说这样的测试并不能反映真实的情况,因为在实际的开发中很少会在一个方法中构造 / 拼接一个长度为 10*1000000 的字符串的。更通常的情况是在一个方法中构造一个不太长的串,但该方法将被大量的,反复的调用。 OK, 我们可以修改一下测试程序来放映这种情况。
新程序中 contactWith…. 方法用来拼接一个不太长的字符串,该方法被 use…. 方法反复调用十万次,并记录总的调用时间。程序如下:
1. 使用 StringBuffer
2. 使用 StringBuilder
测试结果:
No. |
StringBuffer |
StringBuilder |
StringBuffer(int) |
StringBuilder(int) |
1 |
188 |
156 |
140 |
109 |
2 |
187 |
172 |
141 |
125 |
3 |
188 |
172 |
125 |
110 |
4 |
188 |
172 |
141 |
110 |
5 |
187 |
172 |
125 |
110 |
6 |
188 |
172 |
125 |
109 |
7 |
172 |
172 |
125 |
125 |
8 |
188 |
157 |
125 |
110 |
9 |
203 |
172 |
125 |
110 |
10 |
188 |
172 |
125 |
109 |
平均值 |
187.7 |
168.9 |
129.7 |
112.7 |
在这种情况下, StringBuffer 与 StringBuilder 的性能差别为: 11.13% 和 15.08% (使用 int 构造函数);而用不同的构造函数的性能差差异分别达到: 44.71% ( StringBuffer )和 49.87% ( StringBuilder )。并且为 StringBuffer 指定容量(使用 StirngBuffer(int) )比不指定容量的 StringBuilder 的性能高出 30.22% 。
结论:
1. 为了获得更好的性能,在构造 StirngBuffer 或 StirngBuilder 时应尽可能指定它的容量。当然,如果你操作的字符串长度不超过 16 个字符就不用了。
2. 相同情况下使用 StirngBuilder 相比使用 StringBuffer 仅能获得 10%~15% 左右的性能提升,但却要冒多线程不安全的风险。而在现实的模块化编程中,负责某一模块的程序员不一定能清晰地判断该模块是否会放入多线程的环境中运行,因此:除非你能确定你的系统的瓶颈是在 StringBuffer 上,并且确定你的模块不会运行在多线程模式下,否则还是用 StringBuffer 吧 J
3. 用好现有的类比引入新的类更重要。很多程序员在使用 StringBuffer 时是不指定其容量的(至少我见到的情况是这样),如果这样的习惯带入 StringBuilder 的使用中,你将只能获得 10 %左右的性能提升(不要忘了,你可要冒多线程的风险噢);但如果你使用指定容量的 StringBuffer ,你将马上获得 45% 左右的性能提升,甚至比不使用指定容量的 StirngBuilder 都快 30% 左右。
特别声明:
1 .本人是基于 Window XP 环境,用 Sun 的 JDK 1.6 完成的以上测试。测试的结果是否能反映其它操作系统(如 Linux, Unix 等)和不同的 JDK (IBM, Weblogic 等 ) 的情况就不得而知,有兴趣的网友可以在不同的环境中测试,欢迎您告诉我测试结果。
2 .本人也欢迎对本测试的试验设计和样例代码的合理性和完备性进行讨论,但请就事论事。不要扔砖头(西红柿是可以的,不过不要坏的;鸡蛋也可以,但不要臭的,呵呵)
分享到:
相关推荐
String、StringBuilder和StringBuffer的区别 String、StringBuilder和StringBuffer的区别 String、StringBuilder和StringBuffer的区别
String,StringBuffer,StringBuilder区别。几乎所有的应用开发都离不开操作字符串,理解字符串的设计和实现以及相关 工具如拼接类的使用,对写出高质量代码是非常有帮助的
String StringBuffer和StringBuilder 区别之源码解析 从源码角度简单对它们之间的区别进行了验证
stringbuilder用法 String、StringBuilder、StringBuffer 用法比较String、StringBuilder、StringBuffer 用法比较String、StringBuilder、StringBuffer 用法比较String、StringBuilder、StringBuffer 用法比较String...
String ,StringBuffer与StringBuilder的区别
String、StringBuilder以及StringBuffer的区别
java中StringBuilder,StringBuffer,Vector的区别
关于String、StringBuilder、StringBuffer的本质区别,
javase部分String的相关基础知识,String的构造方法总结比较以及各自的应用场景(代码举例),常用的容器类StringBuilder和StringBuffer的关系比较(图示)以及两者的区别联系和具体哪中场景下用哪个类。
主要介绍了java String、StringBuilder和StringBuffer的区别详解,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧
主要针对Java中两个常用的操作字符串的类 StringBuilder和StringBuffer进行源码分析,感兴趣的小伙伴们可以参考一下
String,StringBuilder,StringBuffer
介绍了String、StringBuffer、StringBuilder的使用方法,以及理论知识
String及StringBuffer和StringBuilder的区别
StringBuffer:字符创变量 StringBuilder:字符创变量 从上面的名字可以看到,String是“字符创常量”,也就是不可改变的对象。对于这句话的理解你可能会产生这样一个疑问 ,比如这段代码:
目录StringStringBuffer与StringBuilder总结 String ...StringBuffer与StringBuilder 这两者基本相似,都是可变字符串对象,主要区别为线程是否安全。 总结 最后,引用菜鸟教程下的总结以及相关评论: S
我们先用一张表格来回顾一下Java中String,StringBuilder,StringBuffer关于值可变性、线程安全性、时间性能排序、常用场景等主要区别。见下表: 数据类型 值可变性 线程安全性 时间性能排名 常用场景 String...
String、StringBuffer 和 StringBuilder 的区别 碎碎念 简单认识这三个对象 String StringBuffer StringBuilder 深入理解 String、StringBuilder、StringBuffer 从设计角度理解 String StringBuilder StringBuffer ...
//true}补充:String对象的intern方法会得到字符串对象在常量池中对应的版本的引用(如果常量池中有一个字符串与String对象的equals结果是
主要介绍了Java 中String StringBuilder 与 StringBuffer详解及用法实例的相关资料,需要的朋友可以参考下