随笔-8  评论-39  文章-0  trackbacks-0

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

 

    private static final int COUNT = 1000000;

    private static final String TEMPLATE = "0123456789" ;

    public static void useStringBuffer() {

       StringBuffer bf = new StringBuffer( "" );

       String target = null ;

       long start = System.currentTimeMillis();

       for ( int i = 0; i < COUNT ; i++) {

           bf.append( TEMPLATE );

       }

       target = bf.toString();

       long end = System.currentTimeMillis();

       System. out .println( "Use StringBuffer, time is " + (end - start));

    }  

 

 

2.  StringBuilder

 

    private static final int COUNT = 1000000;

    private static final String TEMPLATE = "0123456789" ;

    public static void useStringBuilder() {

       StringBuilder bf = new StringBuilder( "" );

       String target = null ;

       long start = System.currentTimeMillis();

       for ( int i = 0; i < COUNT ; i++) {

           bf.append( TEMPLATE );

       }

       target = bf.toString();

       long end = System.currentTimeMillis();

       System. out .println( "Use StringBuilder, time is " + (end - start));

    }

 

 

测试结果:

 

 

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

 

    private static final String TEMPLATE = "0123456789" ;

    private static final int COUNT = 1000000;

    public static void useStringBuffer() {

       StringBuffer bf = new StringBuffer(COUNT * TEMPLATE.length());

       String target = null ;

       long start = System.currentTimeMillis();

       for ( int i = 0; i < COUNT ; i++) {

           bf.append( TEMPLATE );

       }

       target = bf.toString();

       long end = System.currentTimeMillis();

       System. out .println( "Use StringBuffer, time is " + (end - start));

    }  

 

2. StringBuilder

 

    private static final String TEMPLATE = "0123456789" ;

    private static final int COUNT = 1000000;

    public static void useStringBuilder() {

       StringBuilder bf = new StringBuilder(COUNT * TEMPLATE.length());

       String target = null ;

       long start = System.currentTimeMillis();

       for ( int i = 0; i < COUNT ; i++) {

           bf.append( TEMPLATE );

       }

       target = bf.toString();

       long end = System.currentTimeMillis();

       System. out .println( "Use StringBuilder, time is " + (end - start));

    }

 

测试结果:(表格中第一,二组为上一轮测试的结果)

 

 

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

 

    private static final String TEMPLATE = "0123456789" ;

    private static final int COUNT = 100000;

    private static final int COUNT2 = 10;

    public static String contactWithStringBuffer() {

//     StringBuffer bf = new StringBuffer("");

       StringBuffer bf = new StringBuffer( COUNT2 * TEMPLATE .length());

       for ( int i = 0; i < COUNT2 ; i++) {

           bf.append( TEMPLATE );

       }

       return bf.toString();

    }

   

    public static void useStringBuffer() {

       long start = System.currentTimeMillis();

       for ( int i = 0; i < COUNT ; i++) {

           contactWithStringBuffer();

       }

       long end = System.currentTimeMillis();

       System. out .println( "Use StringBuffer, Time is " + (end - start));

    }

 

2.  使用 StringBuilder

 

    private static final String TEMPLATE = "0123456789" ;

    private static final int COUNT = 100000;

    private static final int COUNT2 = 10;

    public static String contactWithStringBuilder() {

//     StringBuilder bf = new StringBuilder("");

       StringBuilder bf = new StringBuilder( COUNT2 * TEMPLATE .length());

       for ( int i = 0; i < COUNT2 ; i++) {

           bf.append( TEMPLATE );

       }

       return bf.toString();

    }

   

    public static void useStringBuilder() {

       long start = System.currentTimeMillis();

       for ( int i = 0; i < COUNT ; i++) {

           contactWithStringBuilder();

       }

       long end = System.currentTimeMillis();

       System. out .println( "Use StringBuilder, Time is " + (end - start));

    }  

 

测试结果:

 

 

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 .本人也欢迎对本测试的试验设计和样例代码的合理性和完备性进行讨论,但请就事论事。不要扔砖头(西红柿是可以的,不过不要坏的;鸡蛋也可以,但不要臭的,呵呵)

3 .今天是情人节,祝大家节日快乐,有情人终成眷属!

posted on 2007-02-14 08:00 Jini 阅读(1877) 评论(14)  编辑  收藏 所属分类: JDK相关

评论:
# re: 一个关于StringBuilder与StringBuffer性能的小试验 2007-02-14 10:42 | alysa
Please help to answer the question as below,
What method should class extending String override?
  回复  更多评论
  
# re: 一个关于StringBuilder与StringBuffer性能的小试验[未登录] 2007-02-14 11:08 | jini
@alysa

The String is final class, no class can be extend it.

hehe... I am not sure I answer your question. If no, please give me more detail about your question.  回复  更多评论
  
# re: 一个关于StringBuilder与StringBuffer性能的小试验 2007-02-14 13:36 | cuiyi
顶一个!希望以后能再次看到你的好贴!  回复  更多评论
  
# re: 一个关于StringBuilder与StringBuffer性能的小试验 2007-02-14 13:51 | Jini
@cuiyi

谢谢你的鼓励 :)

其实我只是把日常工作和学习的心得体会纪录下来与大家分享,希望大家能共同学习,共同进步,呵呵。
  回复  更多评论
  
# re: 一个关于StringBuilder与StringBuffer性能的小试验 2007-02-15 08:40 | 不好说
StringBuffer和StringBuilder在toString这个方法上处理不一样。

StringBuffer主要用于在多线程并发存贮同一个实例上才有优势以。
web编程很少出现这种情况。
  回复  更多评论
  
# re: 一个关于StringBuilder与StringBuffer性能的小试验[未登录] 2007-02-15 10:21 | jini
谢谢你的回复!

如果是局部变量,应该不存在线程安全的问题。但可能会面临重复构造对象的问题,就像每次调用contactWith....方法都会构造一个新的StringBuffer或StringBuilder,为避免构造大量的对象,可能会考虑一些简单的重用对象的方法,这时就有可能引入多线程的问题了。  回复  更多评论
  
# re: 一个关于StringBuilder与StringBuffer性能的小试验 2007-02-15 19:18 | BeanSoft
博主这种认真的态度偶太敬佩了!!! 现在的大牛们总是随口就是优化, 性能, 集群, Cache ... 唉, 不知道他们自己试过了没.  回复  更多评论
  
# re: 一个关于StringBuilder与StringBuffer性能的小试验 2007-02-15 20:23 | 喜来了
其实StringBuilder就是删除StringBuffer的synchronized定义的一个新类, 在单一线程的情况,两者的差别取决synchronized和非synchronized函数的差别. 由于Java的线程是native的, 应该和操作系统很有关系。按搂主(初来乍到,不知道怎么称呼,叫错了勿怪)的测试,在Windows XP下相差不大,不知道在其他系统下怎么样。  回复  更多评论
  
# re: 一个关于StringBuilder与StringBuffer性能的小试验 2007-02-15 21:02 | Jini
@BeanSoft

谢谢你的回复! 以后有机会多交流 - Good Good Study, Day Day Up :)

  回复  更多评论
  
# re: 一个关于StringBuilder与StringBuffer性能的小试验 2007-02-15 21:09 | Jini
@喜来了

谢谢你的回复! 你分析的有道理,不过我没有在其它环境测试过,有时间补上,说不定又有些不同的认识了,呵呵.....  回复  更多评论
  
# re: 一个关于StringBuilder与StringBuffer性能的小试验[未登录] 2007-02-16 21:22 | nake
认真仔细的程序员重视能令人佩服和值得尊重.  回复  更多评论
  
# re: 一个关于StringBuilder与StringBuffer性能的小试验 2007-03-17 11:00 | kun
非常严谨直观的对比。
感谢分享。  回复  更多评论
  
# re: 一个关于StringBuilder与StringBuffer性能的小试验 2007-03-17 13:12 | kun
请教:
1.如果测试时StringBuilder和String执行的不是附加,而是循环赋新值操作呢?
(StringBuilder必须通过replace或者先delete再附加来实现重新赋值?)
2.声明一个String和声明一个StringBuilder再toString哪个开销比较大?  回复  更多评论
  
# re: 一个关于StringBuilder与StringBuffer性能的小试验 2011-11-28 21:27 | xinyonda
mark  回复  更多评论
  

只有注册用户登录后才能发表评论。


网站导航: