前两天面试有问,string跟stringbuffer还有stringbuilder的效率以及区别问题
回来写了个测试程序:
测试实例
这是main方法
public static void main(String[] args) {
long time=System.currentTimeMillis();
do1();
long time1=System.currentTimeMillis();
System.out.println("string:"+(time1-time));
do2();
long time2=System.currentTimeMillis();
System.out.println("stringbuffer:"+(time2-time1));
do3();
long time3=System.currentTimeMillis();
System.out.println("stringbuilder:"+(time3-time2));
}
这是do1(string)
private static void do1() {
String a="";
for (int i = 0; i <5; i++) {
a+="a"+"b"+"c"+"d";
}
}
do2(stringbuffer)
private static void do2() {
StringBuffer a=new StringBuffer();
for (int i = 0; i <5; i++) {
a.append("a");
a.append("b");
a.append("c");
a.append("d");
}
}
do3(stringbuilder)
private static void do3() {
StringBuilder a=new StringBuilder();
for (int i = 0; i <5; i++) {
a.append("a");
a.append("b");
a.append("c");
a.append("d");
}
}
先运行一下,结果如下。
循环次数太小,再把次数上调到一百试试。
然后一千。
还是不怎么明显,这次一万应该差不多了。
十万:
二十万:
小结
string的效率明显低下。string的拼接每次相加其实都相当于new了一个新对象,从常量池里面拿出来后新建对象。然后一直占着内存等待gc回收,但是gc回收机制不是即时的,这就导致了大量资源占着从而效率低下。
stringbuffer和stringbuilder每次操作的都是同一个对象
String采用连接运算符(+)效率低下,都是循环、大批量数据情况造成的,每做一次"+"就产生个StringBuilder对象,然后append后就扔掉。下次循环再到达时重新产生个StringBuilder对象,然后append字符串,如此循环直至结束。如果我们直接采用StringBuilder对象进行append的话,我们可以节省创建和销毁对象的时间。如果只是简单的字面量拼接或者很少的字符串拼接,性能都是差不多的。
关于stringbuilder和stringbuffer的选择
String是不可变类,所以是线程安全的。
1、所有不可变类都是线程安全的,线程安全的类不一定是不可变类,如StringBuffer是可变类,靠锁实现线程安全。
2、StringBuffer方法上都加了synchronized,StringBuilder没有,StringBuilder在多线程情况下是会出现问题,但是线程安全 线程非安全 指的是你业务环境需要线程安全考虑不考虑。多并发网络编程这块会考虑这些。
在Java语言中,线程是一种特殊的对象,它必须由Thread类或其子(孙)类来创建。通常有两种方法来创建线程:其一,使用型构为Thread(Runnable)的构造子将一个实现了Runnable接口的对象包装成一个线程。其二,从Thread类派生出子类并重写run方法,使用该子类创建的对象即为线程。值得注意的是Thread类已经实现了Runnable接口,因此,任何一个线程均有它的run方法,而run方法中包含了线程所要运行的代码。线程的活动由一组方法来控制。Java语言支持多个线程的同时执行,并提供多线程之间的同步机制(关键字为synchronized)。
结论
如果字符串不需要频繁变动的话,三个选哪个都无所谓的。如果变动频繁选哪个,其实大部分string操作都是在单线程下进行的,如果有特殊需要再选用线程安全的stringbuffer,基本上可以直接用stringbuilder的。