从源码角度简单看StringBuilder和StringBuffer的异同(全面解析)
概述
StringBuilder和StringBuffer是两个容易混淆的概念,本文从源码入手,简单看二者的异同。
容易知道的是,这两者有一个是线程安全的,而且线程安全的那个效率低。
javadoc里面的说明
javadoc是写源码的人写的注释,先看javadoc。
StringBuilder
Amutablesequenceofcharacters.ThisclassprovidesanAPIcompatiblewithStringBuffer,butwithnoguaranteeofsynchronization.Thisclassisdesignedforuseasadrop-inreplacementforStringBufferinplaceswherethestringbufferwasbeingusedbyasinglethread(asisgenerallythecase).Wherepossible,itisrecommendedthatthisclassbeusedinpreferencetoStringBufferasitwillbefasterundermostimplementations.
TheprincipaloperationsonaStringBuilderaretheappendandinsertmethods,whichareoverloadedsoastoacceptdataofanytype.Eacheffectivelyconvertsagivendatumtoastringandthenappendsorinsertsthecharactersofthatstringtothestringbuilder.Theappendmethodalwaysaddsthesecharactersattheendofthebuilder;theinsertmethodaddsthecharactersataspecifiedpoint.
Forexample,ifzreferstoastringbuilderobjectwhosecurrentcontentsare"start",thenthemethodcallz.append("le")wouldcausethestringbuildertocontain"startle",whereasz.insert(4,"le")wouldalterthestringbuildertocontain"starlet".
Ingeneral,ifsbreferstoaninstanceofaStringBuilder,thensb.append(x)hasthesameeffectassb.insert(sb.length(),x).
Everystringbuilderhasacapacity.Aslongasthelengthofthecharactersequencecontainedinthestringbuilderdoesnotexceedthecapacity,itisnotnecessarytoallocateanewinternalbuffer.Iftheinternalbufferoverflows,itisautomaticallymadelarger.
InstancesofStringBuilderarenotsafeforusebymultiplethreads.Ifsuchsynchronizationisrequiredthenitisrecommendedthatjava.lang.StringBufferbeused.
Unlessotherwisenoted,passinganullargumenttoaconstructorormethodinthisclasswillcauseaNullPointerExceptiontobethrown.
Since:
1.5
Author:
MichaelMcCloskey
SeeAlso:
java.lang.StringBuffer
java.lang.String
StringBuffer
Athread-safe,mutablesequenceofcharacters.AstringbufferislikeaString,butcanbemodified.Atanypointintimeitcontainssomeparticularsequenceofcharacters,butthelengthandcontentofthesequencecanbechangedthroughcertainmethodcalls.
Stringbuffersaresafeforusebymultiplethreads.Themethodsaresynchronizedwherenecessarysothatalltheoperationsonanyparticularinstancebehaveasiftheyoccurinsomeserialorderthatisconsistentwiththeorderofthemethodcallsmadebyeachoftheindividualthreadsinvolved.
TheprincipaloperationsonaStringBufferaretheappendandinsertmethods,whichareoverloadedsoastoacceptdataofanytype.Eacheffectivelyconvertsagivendatumtoastringandthenappendsorinsertsthecharactersofthatstringtothestringbuffer.Theappendmethodalwaysaddsthesecharactersattheendofthebuffer;theinsertmethodaddsthecharactersataspecifiedpoint.
Forexample,ifzreferstoastringbufferobjectwhosecurrentcontentsare"start",thenthemethodcallz.append("le")wouldcausethestringbuffertocontain"startle",whereasz.insert(4,"le")wouldalterthestringbuffertocontain"starlet".
Ingeneral,ifsbreferstoaninstanceofaStringBuffer,thensb.append(x)hasthesameeffectassb.insert(sb.length(),
Wheneveranoperationoccursinvolvingasourcesequence(suchasappendingorinsertingfromasourcesequence),thisclasssynchronizesonlyonthestringbufferperformingtheoperation,notonthesource.NotethatwhileStringBufferisdesignedtobesafetouseconcurrentlyfrommultiplethreads,iftheconstructorortheappendorinsertoperationispassedasourcesequencethatissharedacrossthreads,thecallingcodemustensurethattheoperationhasaconsistentandunchangingviewofthesourcesequenceforthedurationoftheoperation.Thiscouldbesatisfiedbythecallerholdingalockduringtheoperation'scall,byusinganimmutablesourcesequence,orbynotsharingthesourcesequenceacrossthreads.
Everystringbufferhasacapacity.Aslongasthelengthofthecharactersequencecontainedinthestringbufferdoesnotexceedthecapacity,itisnotnecessarytoallocateanewinternalbufferarray.Iftheinternalbufferoverflows,itisautomaticallymadelarger.
Unlessotherwisenoted,passinganullargumenttoaconstructorormethodinthisclasswillcauseaNullPointerExceptiontobethrown.
AsofreleaseJDK5,thisclasshasbeensupplementedwithanequivalentclassdesignedforusebyasinglethread,StringBuilder.TheStringBuilderclassshouldgenerallybeusedinpreferencetothisone,asitsupportsallofthesameoperationsbutitisfaster,asitperformsnosynchronization.
Since:
JDK1.0
Author:
ArthurvanHoff
SeeAlso:
java.lang.StringBuilder
java.lang.String
javadoc小结
从上面可以看出:
StringBuffer和StringBuilder都可以认为是可变的String。
StringBuffer是线程安全的,先出现,在JDK1.0的时候就有了。
StringBuilder是非线程安全的,后出现,在JDK1.5才有。
二者的接口完全一样,StringBuilder更快。
其实,正常的用,知道这几点就好了,不过还是想看看源码里面怎么实现的。
源码
如何实现线程安全
看源码可以知道,这二者都继承了一个抽象类AbstractStringBuilder
publicfinalclassStringBuffer extendsAbstractStringBuilder implementsjava.io.Serializable,CharSequence publicfinalclassStringBuilder extendsAbstractStringBuilder implementsjava.io.Serializable,CharSequence
代码量也不是很大,StringBuilder440行代码,StringBuffer718行代码,最多的是AbstractStringBuilder,一共1440行代码。
看代码从几个角度看,一是一些关键的内部结构是怎么样的,二是我们常用的函数是怎么实现的。因为String是不可变的,是放在常量池里面的。猜测StringBuilder和StringBuffer应该是用char数组实现的。
/** *Thevalueisusedforcharacterstorage. */ char[]value; /** *Thecountisthenumberofcharactersused. */ intcount;
可以看出,用value存数据,用一个count表示长度。
看几个常见方法的不同实现。
StringBuffer
@Override publicsynchronizedStringBufferappend(Stringstr){ toStringCache=null; super.append(str); returnthis; } /** *@throwsStringIndexOutOfBoundsException{@inheritDoc} */ @Override publicsynchronizedStringBufferinsert(intoffset,Stringstr){ toStringCache=null; super.insert(offset,str); returnthis; } @Override publicsynchronizedStringtoString(){ if(toStringCache==null){ toStringCache=Arrays.copyOfRange(value,0,count); } returnnewString(toStringCache,true); }
StringBuilder
@Override publicStringBuilderappend(Stringstr){ super.append(str); returnthis; } /** *@throwsStringIndexOutOfBoundsException{@inheritDoc} */ @Override publicStringBuilderinsert(intoffset,Stringstr){ super.insert(offset,str); returnthis; } @Override publicStringtoString(){ //Createacopy,don'tsharethearray returnnewString(value,0,count); }
由代码可知,大部分情况想,StrinbBuffer只是增加了一个synchronized关键字来保证线程安全。但toString方法不同,这个后面再说。
初始化大小和如何增长
既然实际上是一个数组,那么数组开始的大小和增长的方法就很重要,我们通过代码看一下。
/** *Constructsastringbufferwithnocharactersinitandan *initialcapacityof16characters. */ publicStringBuffer(){ super(16); } /** *Constructsastringbuilderwithnocharactersinitandan *initialcapacityof16characters. */ publicStringBuilder(){ super(16); }
可以看到,这两个默认构造函数都说明默认的数组大小是16。
为什么是16呢?我没想明白。
接下来关心如何增长的?我们看看append的实现
publicAbstractStringBuilderappend(Stringstr){ if(str==null) returnappendNull(); intlen=str.length(); ensureCapacityInternal(count+len); str.getChars(0,len,value,count); count+=len; returnthis; } /** *ThismethodhasthesamecontractasensureCapacity,butis *neversynchronized. */ privatevoidensureCapacityInternal(intminimumCapacity){ //overflow-consciouscode if(minimumCapacity-value.length>0) expandCapacity(minimumCapacity); } /** *ThisimplementstheexpansionsemanticsofensureCapacitywithno *sizecheckorsynchronization. */ voidexpandCapacity(intminimumCapacity){ intnewCapacity=value.length*2+2; if(newCapacity-minimumCapacity<0) newCapacity=minimumCapacity; if(newCapacity<0){ if(minimumCapacity<0)//overflow thrownewOutOfMemoryError(); newCapacity=Integer.MAX_VALUE; } value=Arrays.copyOf(value,newCapacity); }
上面三个方法说明了如何扩容。
把当前的容量*2+2
如果新增加的长度大于这个值,则设为新增加的值
如果溢出,则抛出OutOfMemoryError
StringBuffer中toString的实现
/** *AcacheofthelastvaluereturnedbytoString.Cleared *whenevertheStringBufferismodified. */ privatetransientchar[]toStringCache; @Override publicsynchronizedStringBufferappend(Stringstr){ toStringCache=null; super.append(str); returnthis; } @Override publicsynchronizedStringtoString(){ if(toStringCache==null){ toStringCache=Arrays.copyOfRange(value,0,count); } returnnewString(toStringCache,true); }
可以看到,定义了一个数组toStringCache,每次数据有变化,都把这个设置为null。在toString的时候,再重新从当前数据里面拿。
transient关键字是为了避免这个数组被序列化。
小结
其实java本身的源码写的还是比较简单的,学习知识如果能从源码入手,能够更深入的理解很多原理。本文只是简单的列举了一部分源码,简单说明StringBuffer和StringBuilder的异同。有兴趣的朋友可以自己看一下。
以上这篇从源码角度简单看StringBuilder和StringBuffer的异同(全面解析)就是小编分享给大家的全部内容了,希望能给大家一个参考,也希望大家多多支持毛票票。