在总体的框架设计确定以后多注意一些编程细节积少成多可以获得更佳的性能让程序跑的更快! 前些天同事优化代码时提到了String和StringBuffer仔细想想发现自己也就是知道个大概所以查了 一下资料文章中有错误的地方你可一定要告诉我啊呵呵^+^ 一介绍 String:非可变类(immutable)一旦创建就不能再被改变 StringBuffer:可变类创建之后可以被改变 何谓非可变类? 简单说非可变类的实例是不能被修改的每个实例中包含的信息都必须在该实例创建的时候就提供出来 并且在对象的整个生存周期内固定不变 非可变类好处状态单一对象简单便于维护通常是线程安全的用户可以共享非可变对象甚至可 以共享它们的内部信息 二创建字符串 两种方法 String s = hello; String s = new String(hello) 哪种方式性能更好? 例: //create String without new keyword longstartTime=SystemcurrentTimeMillis() for(inti=;i<;i++){ String str= hello; } longendTime=SystemcurrentTimeMillis() Systemoutprintln(create String without new keyword : +(endTimestartTime)+ milli seconds) //create String with new keyword longstartTime=SystemcurrentTimeMillis() for(inti=;i<;i++){ String str= newString(hello) } longendTime=SystemcurrentTimeMillis() Systemoutprintln(create String with new keyword : +(endTimestartTime)+ milli seconds) 输出结果为(注程序的输出结果也许和你的结果不同但是总体趋势应该是一致的) create String withoutnewkeyword :milli seconds create String withnewkeyword :milli seconds 结论创建字符串变量时尽可能不使用new关键字 说明 虽然两个语句都是返回一个String对象的引用但是jvm对两者的处理方式是不一样的 对于第一种不用new关键字创建String对象 JVM首先会在内部维护的滞留字符串中通过String的equels方法查找是对象池中是否存放有该 String对象如果有返回已有的String对象给用户而不会在heap中重新创建一个新的String对象如 果对象池中没有该String对象JVM则在heap中创建新的String对象将其引用返回给用户同时将该引 用添加至滞留字符串中 对于第二种使用new关键字创建String对象 JVM会马上在heap中创建一个String对象然后将该对象的引用返回给用户JVM是不会主动把该 对象放到滞留字符串里面的除非程序调用 String的intern()方法 JVM为字符串自变量维护一些唯一的String对象程序员不需要为字符串自变量而发愁但是使用new关键 字可能会在内存中创建重复的String对象你不必为此而烦恼intern()方法可以帮你解决问题 Stringintern()检查字符串对象的存在性如果需要的字符串对象已经存在那么它会将引用指向已 经存在的字符串对象而不是重新创建一个 例: String str= world; /*JVM在滞留字符串中找不到值为world的字符串就在堆上创建一个string对象并将该对象的引用加入到滞留字符串中*/ String str= newString(world) /*JVM在堆上重新创建一个值为world的字符串此时堆上有两个值为world的字符串*/ if(str==str){ Systemoutprintln(str == str) } else{ Systemoutprintln(str != str) } //输出str != str String str= world; /*JVM在发现滞留字符串中存在world对象因此返回str指向的对象给str即str和str是指向同一个对象的引用*/ if(str==str){ Systemoutprintln(str == str) } else{ Systemoutprintln(str != str) } //输出str == str str=strintern() /*此时JVM发现滞留字符串中已经存在world对象因此返回str指向的对象给str即str和str是指向同一个对象的引用*/ if(str==str){ Systemoutprintln(after intern() str == str) } else{ Systemoutprintln(after intern() str != str) } //输出after intern() str == str 结论如果使用new关键字创建了字符串变量则尽可能使用intern()方法 上面的例子执行正是用到了string对象的不可变性质既然string对象一旦创建就不可以改变那么多个 引用指向同一个对象就不会对彼此产生影响 三字符串连接 你可以使用+操作符或者ncat()或者StringBufferappend()等办法来连接多个字符串哪 一种方法性能最佳? 如何选择取决于两种情景 第一种情景需要连接的字符串是在编译期间决定的还是在运行期间决定 第二种情景你使用的是 StringBuffer还是String 通常程序员会认为StringBufferappend()方法会优于+操作符或 ncat()方法但是在一些特定 的情况下这个假想是不成立的 ) 第一种情景编译期间决定VS运行期间决定 //test the string Concatenation longstartTime=SystemcurrentTimeMillis() for(intk=; k<; k++){ String str= this is + a test + for string concatenation; } longendTime=SystemcurrentTimeMillis() Systemoutprintln(string concatenation using + : +(endTimestartTime)+ milli seconds) longstartTime=SystemcurrentTimeMillis() for(intk=; k<; k++){ String str= this is; ncat(a test) ncat(for string concatenation) } longendTime=SystemcurrentTimeMillis() Systemoutprintln(string concatenation using concat() : +(endTimestartTime)+ milli seconds) longstartTime=SystemcurrentTimeMillis() for(intl=; l<; l++){ StringBuffer sb= newStringBuffer() sbappend(this is) sbappend(a test) sbappend(for string concatenation) } longendTime=SystemcurrentTimeMillis() Systemoutprintln(string concatenation using append() : +(endTimestartTime)+ milli seconds) 上面代码的输出结果 string concatenation using+:milli seconds string concatenation using concat() :milli seconds string concatenation using append() :milli seconds 很有趣+操作符比StringBufferappend()方法要快 Why? 这是因为编译器对简单的字符串进行了优化即使使用new关键字来创建String对象的时候也是如此例 如 编译前 String str = this is + a test + for string concatenation; 编译后 String str = this is a test for string concatenation; 这里String对象在编译期间就决定了而StringBuffer对象是在运行期间决定的运行期间决定需要额外的 开销 结论如果字符串在编译期间就可以决定它的值则字符串拼接的时候 +操作符效率更高简单的 认为append()效率高于+是错误的 ) 第二种情景StringBufferVSString //string concatenation using += longstartTime=SystemcurrentTimeMillis() String str= hello; for(inti=; i<; i++){ str+= hello; } longendTime=SystemcurrentTimeMillis() Systemoutprintln(string concatenation using += : +(endTimestartTime)+ milli seconds) //string concatenation using append() longstartTime=SystemcurrentTimeMillis() StringBuffer sb= newStringBuffer(hello) for(inti=; i<; i++){ sbappend(hello) } longendTime=SystemcurrentTimeMillis() Systemoutprintln(string concatenation using append() : +(endTimestartTime)+ milli seconds) 上面代码的输出结果 string concatenation using+=:milli seconds string concatenation using append() :milli seconds 结论避免使用+=来构造字符串 虽然两者都是在运行期间决定字符串对象但是使用+=操作符会产生更多的临时对象 在上例中由于String类是不可变的所以进行字符串拼接的时候每循环一次就会产生临时对象来保存 str和hello的值之后创建一个临时的StringBuffer对象并调用其append()方法来完成字符串的 拼接最后调用toString()方法将临时StringBuffer对象转为String再赋值给str此时str已经改变 指向了新的对象 ) 第三种情景设置StringBuffer的容量来提升性能 longstartTime=SystemcurrentTimeMillis() StringBuffer sb= newStringBuffer(hello) for(inti=; i<; i++){ sbappend(hello) } longendTime=SystemcurrentTimeMillis() Systemoutprintln(string concatenation using append() : +(endTimestartTime)+ milli seconds) //set the StringBuffer capacity longstartTime=SystemcurrentTimeMillis() StringBuffer sb= newStringBuffer(hello) sbensureCapacity() for(inti=; i<; i++){ sbappend(hello) } longendTime=SystemcurrentTimeMillis() Systemoutprintln(string concatenation using append() after set the StringBuffer capacity : milli seconds) 上面代码的输出结果 string concatenation using append() :milli seconds string concatenation using append() after set the StringBuffer capacity :milli seconds 结论 声明StringBuffer对象的时候指定合适的capacity会提升程序性能 )使用StringBuffer的构造函数来设定它的初始化容量StringBuffer(int length) )使用ensureCapacity(int minimumcapacity)方法在StringBuffer对象创建之后设置它的容量 首先我们看看StringBuffer的缺省行为然后再找出一条更好的提升性能的途径 StringBuffer的缺省行为 StringBuffer在内部维护一个字符数组当你使用缺省的构造函数来创建StringBuffer对象的时候 StringBuffer的容量被初始化为个字符也就是说缺省容量就是个字符当StringBuffer达到最大容 量的时候它会将自身容量增加到当前的倍再加也就是(*旧值+) 如果你使用缺省值初始化之后接着往里面追加字符在你追加到第(原文是其实是错误的因为在追加到第个字符的时候容量不会发生变化很抱歉以后会更严谨一些^+^)个字符的时候它会将容量增加 到(*+)当追加到个字符的时候就会将容量增加到(*+)无论何事只要StringBuffer 到达它的最大容量它就不得不创建一个新的字符数组然后重新将旧字符和新字符都拷贝一遍所以给 StringBuffer设置一个合理的初始化容量值会提升程序的性能 但是为什么容量变化的时候是*旧值+呢?有谁能告诉我么?查资料的时候没有找到 附查资料的过程中发现了jdk 还提供了StringBuilder类我是没有用过不过也介绍一下好了 JavalangStringBuffer 线程安全的可变字符序列类似于 String 的字符串缓沖区但不能修 改可将字符串缓沖区安全地用于多个线程可以在必要时对这些方法进行同步因此任意特定实例上的 所有操作就好像是以串行顺序发生的该顺序与所涉及的每个线程进行的方法调用顺序一致 每个字符串缓沖区都有一定的容量只要字符串缓沖区所包含的字符序列的长度没有超出此容量 就无需分配新的内部缓沖区数组如果内部缓沖区溢出则此容量自动增大从 JDK 开始为该 类增添了一个单个线程使用的等价类即 StringBuilder 与该类相比通常应该优先使用 StringBuilder 类因为它支持所有相同的操作但由于它不执行同步所以速度更快 但是如果将 StringBuilder 的实例用于多个线程是不安全的需要这样的同步则建议使用 StringBuffer |