你不知道的事— —关于String,StringBuilder,StringBuffer

原文地址http://mp.weixin.qq.com/s/eNpYX5cyLp9pHB36RndiCg


最近在学习Java的时候,遇到了这样一个问题:String,StringBuilder以及StringBuffer这三个类之间有什么区别呢?

String,StringBuilder,StringBuffer这三个类之间的区别主要是在两个方面:运行速度线程安全

1.首先说运行速度(执行速度),在这方面运行速度快慢为:StringBuilder > StringBuffer > String

String最慢的原因:String为字符串常量,而StringBuilder和StringBuffer均为字符串变量,即String对象一旦创建之后该对象是不可更改的,但后两者的对象是变量,是可以更改的。以下面一段代码为例:

String str="abc";
System.out.println(str);
str=str+"de";
System.out.println(str);

如果运行这段代码会发现先输出“abc”,然后又输出“abcde”,好像是str这个对象被更改了,其实这只是一种假象罢了。JVM对于这几行代码是这样处理的,首先创建一个String对象str,并把“abc”赋值给str,然后在第三行中,JVM又创建了一个新的对象也名为str,再把原来的str的值和“de”加起来再赋值给新的str,而原来的str就会被JVM的垃圾回收机制(GC)给回收掉了,所以,str实际上并没有被更改,也就是前面说的String对象一旦创建之后就不可更改了。所以,Java中对String对象进行的操作实际上是一个不断创建新的对象并且将旧的对象回收的一个过程,所以执行速度很慢。

而StringBuilder和StringBuffer的对象是变量,对变量进行操作就是直接对该对象进行更改,而不进行创建和回收的操作,所以速度要比String快很多。

另外,有时候我们会这样对字符串进行赋值:

String str="abc"+"de";
StringBuilder stringBuilder=new StringBuilder().append("abc").append("de");
System.out.println(str);
System.out.println(stringBuilder.toString());

这样输出结果也是“abcde”和“abcde”,但是String的速度却比StringBuilder的反应速度要快很多,这是因为第1行中的操作和String str="abcde";是完全一样的,所以会很快,而如果写成下面这种形式:

String str1="abc";
String str2="de";
String str=str1+str2;

那么JVM就会像上面说的那样,不断的创建、回收对象来进行这个操作了。速度就会很慢。

2.再来看线程安全,StringBuilder是线程不安全的,而StringBuffer是线程安全的。

如果一个StringBuffer对象在字符串缓冲区被多个线程使用时,StringBuffer中很多方法可以带有synchronized关键字,所以可以保证线程是安全的;但StringBuilder的方法则没有该关键字,所以不能保证线程安全,有可能会出现一些错误的操作。所以如果要进行的操作是多线程的,那么就要使用StringBuffer,但是在单线程的情况下,还是建议使用速度比较快的StringBuilder。

3. 总结一下:

String:适用于少量的字符串操作的情况


StringBuilder:适用于单线程下在字符缓冲区进行大量操作的情况


StringBuffer:适用多线程下在字符缓冲区进行大量操作的情况




【java面试题】StringBuilder和StringBuffer 以及我们最早遇见的String 类有那些区别呢?在不同的场合下我们应该用哪个呢?

public static void main(String[] args) throws ParseException, IOException {
       String words="123456789";
       StringBuffer sb=new StringBuffer();
       sb.append(words);
       for (int i = words.length()-3; i >0; i-=3) {
           sb.insert(i,",");    
       }
       System.out.println(sb.toString());
}

简要的说,String类型和StringBuffer类型的主要性能区别其实在于String 是不可变的对象,因此在每次对String类型进行改变的时候其实都等同于生成了一个新的String对象,然后将指针指向新的String对象,所以经常改变内容的字符串最好不要用String,因为每次生成对象都会对系统性能产生影响,特别当内存中无引用对象多了以后,JVM 的GC就会开始工作,那速度是一定会相当慢的。这里尝试举个不是很恰当的例子:

String Str = “abc”; 
For(int i = 0  i < 10000  i++) 

Str + = “def”; 
}

如果是这样的话,到这个for 循环完毕后,如果内存中的对象没有被GC 清理掉的话,内存中一共有上万个了,惊人的数目,而如果这是一个很多人使用的系统,这样的数目就不算很多了,所以大家使用的时候一定要小心。

而如果是使用StringBuffer类则结果就不一样了,每次结果都会对StringBuffer 对象本身进行操作,而不是生成新的对象,再改变对象引用。所以在一般情况下我们推荐使用StringBuffer,特别是字符串对象经常改变的情况下。而在某些特别情况下,String 对象的字符串拼接其实是被JVM 解释成了StringBuffer对象的拼接,所以这些时候String 对象的速度并不会比StringBuffer 对象慢,而特别是以下的字符串对象生成中,String 效率是远要比StringBuffer 快的:

String Str = “This is only a” + “ simple” + “ test”; 
StringBuffer Sb= new StringBuilder(“This is only a”).append(“ simple”).append(“ test”);

你会很惊讶的发现,生成String Str 对象的速度简直太快了,而这个时候StringBuffer 居然速度上根本一点都不占优势。其实这是JVM 的一个把戏,在JVM 眼里,这个String Str = “This is only a” + “ simple” + “test”; 其实就是:String Str = “This is only a simpletest”; 所以当然不需要太多的时间了。但大家这里要注意的是,如果你的字符串是来自另外的String 对象的话,速度就没那么快了,譬如:

String S2 = “This is only a”; 
String S3 = “ simple”; 
String S4 = “ test”; 
String S1 = S2 +S3 + S4;

这时候JVM 会规规矩矩的按照原来的方式去做,S1 对象的生成速度就不像刚才那么快了,一会儿我们可以来个测试作个验证。由此我们得到第一步结论:在大部分情况下StringBuffer > String 而StringBuilder 跟他们比又怎么样呢?

先简单介绍一下,StringBuilder 是JDK5.0 中新增加的一个类,它跟StringBuffer 的区别看下面的介绍: 

Java.lang.StringBuffer线程安全的可变字符序列。类似于tring 的字符串缓冲区,但不能修改。可将字符串缓冲区安全地用于多个线程。可以在必要时对这些方法进行同步,因此任意特定实例上的所有操作就好像是以串行顺序发生的,该顺序与所涉及的每个线程进行的方法调用顺序一致。每个字符串缓冲区都有一定的容量。只要字符串缓冲区所包含的字符序列的长度没有超出此容量,就无需分配新的内部缓冲区数组。如果内部缓冲区溢出,则此容量自动增大。从JDK 5.0 开始,为该类增添了一个单个线程使用的等价类,即StringBuilder 。与该类相比,通常应该优先使用StringBuilder 类,因为它支持所有相同的操作,但由于它不执行同步,所以速度更快。但是如果将StringBuilder 的实例用于多个线程是不安全的。需要这样的同步,则建议使用StringBuffer 。那么下面我们再做一个一般性推导:

在大部分情况下StringBuilder > StringBuffer 因此,根据这个不等式的传递定理:在大部分情况下StringBuilder > StringBuffer > String

implements java.io.Serializable, 
CharSequence ublic final class StringBuilder extends AbstractStringBuilder 
implements java.io.SerializableCharSequence 
public final class StringBuffer 
extends AbstractStringBuilder 
implements java.io.SerializableCharSequence 
public final class StringBuilder extends AbstractStringBuilder 
implements java.io.SerializableCharSequence
可以看到StringBuffer和StringBuilder都继承继承了同一个抽象类。

Java.lang.StringBuffer线程安全的可变字符序列。一个类似于String 的字符串缓冲区,但不能修改。虽然在任意时间点上它都包含某种特定的字符序列,但通过某些方法调用可以改变该序列的长度和内容。可将字符串缓冲区安全地用于多个线程。可以在必要时对这些方法进行同步,因此任意特定实例上的所有操作就好像是以串行顺序发生的,该顺序与所涉及的每个线程进行的方法调用顺序一致。每个字符串缓冲区都有一定的容量。只要字符串缓冲区所包含的字符序列的长度没有超出此容量,就无需分配新的内部缓冲区数组。如果内部缓冲区溢出,则此容量自动增大。

StringBuffer上的主要操作是append 和insert 方法,可重载这些方法,以接受任意类型的数据。每个方法都能有效地将给定的数据转换成字符串,然后将该字符串的字符追加或插入到字符串缓冲区中。append 方法始终将这些字符添加到缓冲区的末端;而insert 方法则在指定的点添加字符。

java.lang.StringBuilder,一个可变的字符序列是5.0新增的。此类提供一个与StringBuffer 兼容的API,但不保证同步,StringBuilder的速度比StringBuffer快。该类被设计用作StringBuffer 的一个简易替换,用在字符串缓冲区被单个线程使用的时候(这种情况很普遍)。两者的方法基本相同。如果要多次操作字符串,使用StringBuffer和StringBuilder会提高效率,但至少在数量级超过百万时,StringBuilder的速度才会体现出来。

本文属于原创,如有疑问请后台留言,如有转载请标注原作者,版权归本公众号所有。如果你喜欢我写的文章请关注微信公众号 Java资源社区,欢迎大家继续关注本公众号的技术博文。如果您觉得本文章对你有所帮助的话,不妨点个赞,您的支持就是我坚持原创的动力。


阅读更多
想对作者说点什么? 我来说一句

没有更多推荐了,返回首页