java中String、StringBuffer、StringBulider是经常要用到的字符串类,那么它们之间到底有什么不同?以及它们的应用场景?
String字符串常量
String 简介
String 类是不可变类,所以String本身的值是不能改变的,任何对String的操作都会引起新的String对象的产生.去看看String类的具体实现你就会发现,用来保存字符串的数组用final修饰:
下面的代码段是String类的部分实现.
private final char value[]; //保存字符串的数组.
如何证明String对象本身的值不可变?
String s="abcd";
s=s+1;
System.out.println(s); //result:abcd1;
这段代码大家一定已经用烂了,从结果来看,我们明明改变了String类型的变量s,为什么说String对象是不可变的呢,其实这是一种欺骗,JVM是这样解析这段代码的:首先创建对象s,赋予abcd,然后再创建一个新的对象s来执行第二行的代码,也就是说我们之前的对象s并没有改变.所以说String类型是不可变的对象了,由于这种机制,每当用String操作字符串时,实际上是不断的创建新的对象,所以效率会比较高,原来的对象会被GC回收掉.看下面这段代码:
String a="aaaaaa";
System.out.println(a.replace('a','b'); //result:bbbbbb
System.out.println(a); //result:aaaaaa
从结果可以看出,执行a.repalce(‘a’,’b’);并没有在a对象上操作,而是创建了一个新的对象. 因为最后a的值还是aaaaaa.
StringBuffer(字符串变量)
StringBuffer简介
操作StringBuffer类型字符串时,每次结果都会对StringBuffer对象本身进行操作,而不是生成新的对象,而改变对象引用,所以一般情况推荐使用StringBuffer,特别是字符串对象经常改变的情况下.
public final class StringBuffer
extends AbstractStringBuilder
implements java.io.Serializable, CharSequence
abstract class AbstractStringBuilder implements Appendable, CharSequence {
char[] value; //保存字符串的字符数组.
上面是StringBuffer的部分实现,大家可以看到,它继承了AbstractStringBuilder,用可变的数组保存要操作的字符串.
怎样证明StringBuffer类型的字符串是在自身上操作?
StringBuffer b=new StringBuffer("aaaaaa");
System.out.println(b.replace(0,6,"bbbbbb"); //result:bbbbbb
System.out.println(b); result:bbbbbb;
从结果可以看出,执行b.replace(0,6,”bbbbbb”,b的内容也变成了bbbbbb,说明,字符串的替换是在b对象本身上进行的.
线程安全的StringBuffer
StringBuffer 对方法加了同步锁,或对调用的方法加了同步锁,所以是线程安全的.部分源码如下:
@Override
public synchronized int length() {
return count;
}
@Override
public synchronized StringBuffer append(Object obj) {
toStringCache = null;
super.append(String.valueOf(obj));
return this;
}
StringBuilder(字符串变量)
StringBuilder简介
操作StringBuilder类型的字符串时,每次结果也都是在对象本身进行操作,不会产生新的对象.部分源码如下:
public final class StringBuilder
extends AbstractStringBuilder
implements java.io.Serializable, CharSequence
abstract class AbstractStringBuilder implements Appendable, CharSequence {
char[] value;保存字符串的字符数组.
从上面的可以看出.它继承了AbstractStringBuilder,用可变的数组保存要操作的字符串.
如何证明StringBulider是在对象本身进行操作?
StringBuilder b=new StringBuffer("aaaaaa");
System.out.println(b.replace(0,6,"bbbbbb"); //result:bbbbbb
从结果可以看出,执行b.replace(0,6,”bbbbbb”,b的内容也变成了bbbbbb,说明,字符串的替换是在b对象本身上进行的.
非线程安全StringBuilder
部分源码如下:
@Override
public int length() {
return count;
}
public AbstractStringBuilder append(Object obj) {
return append(String.valueOf(obj));
}
StringBuilder并没有对方法进行加同步锁,所以是非线程安全的。
StringBuildler与StringBuffer共同点
- StringBuilder与StringBuffer有公共父类AbstractStringBuilder(抽象类)。
- 抽象类与接口的其中一个区别是:抽象类中可以定义一些子类的公共方法,子类只需要增加新的功能,不需要重复写已经存在的方法;而接口中只是对方法的申明和常量的定义。
- StringBuilder、StringBuffer的方法都会调用AbstractStringBuilder中的公共方法,如super.append(…)。只是StringBuffer会在方法上加synchronized关键字,进行同步。
String,StringBuffer,StringBulider效率问题
- 对String类型的字符串操作每次都要构造新的字符串,效率最慢(用一种情况例外).
- StringBuffer,StringBuilder类型的字符串的操作都在自身上操作,减少了系统创建对象的开销,但是Stringbuffer是线程安全的,在并发操作中,对此类型的字符串的操作都是安全的,所以,相对于StringBuilder,由于线程安全要进行加锁—>修改—->释放锁的逻辑,效率比较低. 所以在单线程下优先选择StringBulider.
- 速度从慢到快:String
应用场景
- String 一般用于操作少量的数据.
- 单线程操作字符串缓冲区下操作大量数据StringBuilder;
- 多线程操作字符串缓冲区下操作大量数据StringBuffer;
测试实例
下面是一个String,StringBuffer,StringBuilder速度测试代码:
方法是分别用String,StringBuffer,StringBulider连接2000000个字符串,看其执行时间,由于String特别慢,而StringBulider特别快,所以String连接了20000个,而只有在大量连接操作中,StringBuilder和StringBuffer的效率草拟体现出来,所以StringBuilder和StringBuffer都连接的是2000000个字符串. 最后测试了StringBuffer和StringBulider的赋值操作:
代码如下:
package thread;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
/**
* Created by yang on 16-7-10.
*/
public class StringTest {
public static String BASEINFO = "yang";
public static final int COUNT = 2000000;
/**
* 执行一项String赋值测试
*/
public static void doStringTest() {
String str = new String(BASEINFO);
long starttime = System.currentTimeMillis();
for (int i = 0; i < COUNT/100; i++) {
str = str + "xiyou";
}
long endtime = System.currentTimeMillis();
System.out.println((endtime - starttime)
+ " millis has costed when used String.");
}
/**
* 执行一项StringBuffer赋值测试
*/
public static void doStringBufferTest() {
StringBuffer sb = new StringBuffer(BASEINFO);
long starttime = System.currentTimeMillis();
for (int i = 0; i < COUNT; i++) {
sb = sb.append("miss");
}
long endtime = System.currentTimeMillis();
System.out.println((endtime - starttime)
+ " millis has costed when used StringBuffer.");
}
/**
* 执行一项StringBuilder赋值测试
*/
public static void doStringBuilderTest() {
StringBuilder sb = new StringBuilder(BASEINFO);
long starttime = System.currentTimeMillis();
for (int i = 0; i < COUNT; i++) {
sb = sb.append("miss");
}
long endtime = System.currentTimeMillis();
System.out.println((endtime - starttime)
+ " millis has costed when used StringBuilder.");
}
/**
* 测试StringBuffer遍历赋值结果
*
* @param mlist
*/
public static void doStringBufferListTest(List<String> mlist) {
StringBuffer sb = new StringBuffer();
long starttime = System.currentTimeMillis();
for (String string : mlist) {
sb.append(string);
}
long endtime = System.currentTimeMillis();
System.out.println(sb.toString() + "buffer cost:"
+ (endtime - starttime) + " millis");
}
/**
* 测试StringBuilder迭代赋值结果
*
* @param mlist
*/
public static void doStringBuilderListTest(List<String> mlist) {
StringBuilder sb = new StringBuilder();
long starttime = System.currentTimeMillis();
for (Iterator<String> iterator = mlist.iterator(); iterator.hasNext();) {
sb.append(iterator.next());
}
long endtime = System.currentTimeMillis();
System.out.println(sb.toString() + "builder cost:"
+ (endtime - starttime) + " millis");
}
public static void main(String[] args) {
doStringTest();
doStringBufferTest();
doStringBuilderTest();
List<String> list = new ArrayList<String>();
list.add(" I ");
list.add(" like ");
list.add(" xiyou ");
list.add(" linux ");
list.add(" xingqu ");
list.add(" xiaozu ");
list.add(" . ");
doStringBufferListTest(list);
doStringBuilderListTest(list);
}
}
结果如下:
1779 millis has costed when used String. //连接了20000个字符串
79 millis has costed when used StringBuffer. //连接了2000000个字符串.
53 millis has costed when used StringBuilder. //连接了2000000个字符串.
I like xiyou linux xingqu xiaozu. buffer cost:1 millis //7次循环赋值操作
I like xiyou linux xingqu xiaozu. builder cost:0 millis//7次循环赋值操作