A股上市公司传智教育(股票代码 003032)旗下技术交流社区北京昌平校区

 找回密码
 加入黑马

QQ登录

只需一步,快速开始

© 周志龙 中级黑马   /  2013-10-10 18:38  /  1121 人查看  /  0 人回复  /   0 人收藏 转载请遵从CC协议 禁止商业使用本文

StringBuilder是平常编程中比较常用的JDK下的类之一。在字符串的拼接中发挥着重大的作用。可能很多人对于StringBuilder的认识并不深刻。这里通过我的理解来阐述一下StringBuilder。

1.为什么要使用StringBuilder?

        这里很多人可能会说有“+”写出来的代码可能不是那么雅观,可能算做一个,另外更重要的原因是众所周知的性能。这在很多面试题里会屡试不爽的提在字符串拼接的情况下使用StringBuilder,在for循环下更是如此。why?这里归根结底是String的final特性决定的。如果细心的同学可以看一下String的源码中存储的数组就是final类型的。String本身是无法达到扩容的目的。

        2.为什么StringBuilder可以得到性能上的提升。

        StringBuilder内部也是一个char数组,这和String内部的存储实现无异。下面用具体的代码来看一下使用String和StringBuilder在性能上的差异和性能到底损失在什么地方。
  1. @Test

  2. public void test3(){  
  3.     String s = null;  
  4.     for(int i=0;i<1000;i++){  
  5.         s +=i;  
  6.     }  
  7. }  
  8.    
  9. @Test
  10. public void test4(){  
  11.     StringBuilder builder = new StringBuilder();  
  12.     for(int i=0;i<1000;i++){  
  13.         builder.append(i);  
  14.     }  
  15. }  
  16. @Test
  17. public void test5(){  
  18.     StringBuilder builder = new StringBuilder(3000);  
  19.     for(int i=0;i<1000;i++){  
  20.         builder.append(i);  
  21.     }  
  22. }
复制代码
test3方法
       采用最笨的+操作符,上面已经说String是final类型的,可以看一下中间会产生出多少中间对象,可以简单的算一下,本身循环1000次会有1000个对象,另外实现+操作后又产生1000个对象。最后垃圾回收多少对象?回收1999个对象。只保留最后一个s对象。

        test4方法

        采用不初始化初始容器的方式进行,这也是大部分程序员能做到的。这里会产生多少对象呢?在外面看来是1001个对象,其实不是这样的。这里又要看一下StringBuilder的初始容量是16。当i=13的时候出现了一次扩容,看一下内部的会用到System.arrayCopy来实现一个扩容机制。这里再回到StringBuilder内部的存储是采用char数组实现存储的,而这个char[]的初始容量就是16,这样当i=13的时候会发现原来的数组已经装不下了,这时候怎么办?这时候首先申请一个更大容量的数组,新申请数组的大小是多少呢?是(当前容量的大小+1)*2,也就是34,很多人说我这时候加入的字符串还是大于扩充的容量怎么办,这里做一个处理,就是如果发现加进来的字符串的大小大于当前新申请的容量,就以新加进来的字符串的大小作为申请容易的大小。这里说的有点绕,看代码会清晰很多。

        回到我所说的test4例子,根据上面的分析,其实StringBuilder需要做几次扩容才能完成,这里性能损失会在哪,一个是数组copy,另外一个就是扩容产生的垃圾对象需要JVM回收都会造成性能损耗。算一下这里要扩容几次。要扩容8次,中间就会产生8个中间对象需要JVM回收,而越往后面垃圾对象越大。

       test5方法

       test5在test4上面的改进主要就是初始化容量,这么做的好处就是省去了8次扩容产生的性能损耗和JVM回收垃圾的时间。

      结论

      所以在知道当前你append字符串容量的情况下可以指定其大小,在某些场景下有不少的性能提升。上面的分析同样可以平移到ArrayList,HashMap等内部采用数组实现的结构中。

      3.如何计算需要初始化容量的大小?

      String类型好算一些,length就可以了。像一些int类型,long类型它们需要申请多少空间呢?举一个简单的例子,像int类型的1000在StringBuilder需要占几个?答案是4个,如果去看一下源码就知道,它其实是转成String存,当然内部并没有强转成String,而是用了getChars方法。里面用了一些小技巧来获得一些性能的提升,有兴趣的同学也可以去看一下StringBuilder的实现。

      4.具体的性能有多大的提升?

      这里有兴趣的同学可以去压测一下。

0 个回复

您需要登录后才可以回帖 登录 | 加入黑马