黑马程序员技术交流社区
标题:
StringBuilder源码分析
[打印本页]
作者:
周志龙
时间:
2013-10-10 18:38
标题:
StringBuilder源码分析
StringBuilder是平常编程中比较常用的JDK下的类之一。在字符串的拼接中发挥着重大的作用。可能很多人对于StringBuilder的认识并不深刻。这里通过我的理解来阐述一下StringBuilder。
1.为什么要使用StringBuilder?
这里很多人可能会说有“+”写出来的代码可能不是那么雅观,可能算做一个,另外更重要的原因是众所周知的性能。这在很多面试题里会屡试不爽的提在字符串拼接的情况下使用StringBuilder,在for循环下更是如此。why?这里归根结底是String的final特性决定的。如果细心的同学可以看一下String的源码中存储的数组就是final类型的。String本身是无法达到扩容的目的。
2.为什么StringBuilder可以得到性能上的提升。
StringBuilder内部也是一个char数组,这和String内部的存储实现无异。下面用具体的代码来看一下使用String和StringBuilder在性能上的差异和性能到底损失在什么地方。
@Test
public void test3(){
String s = null;
for(int i=0;i<1000;i++){
s +=i;
}
}
@Test
public void test4(){
StringBuilder builder = new StringBuilder();
for(int i=0;i<1000;i++){
builder.append(i);
}
}
@Test
public void test5(){
StringBuilder builder = new StringBuilder(3000);
for(int i=0;i<1000;i++){
builder.append(i);
}
}
复制代码
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.具体的性能有多大的提升?
这里有兴趣的同学可以去压测一下。
欢迎光临 黑马程序员技术交流社区 (http://bbs.itheima.com/)
黑马程序员IT技术论坛 X3.2