声明:本文使用JDK1.8
在Java中,对于字符串的操作有这三种:String、StringBuilder、StringBuffer。这三者的效率是:StringBuilder > StringBuffer > String。
1 | String a = "abc"; |
String
先来看下String的源码,如图所示:
从图中我们可以看出,String
是由 char
数组构成的,而且有 final
关键字修饰,这说明 String
类型的对象是不可以改变的。那么,平时我们使用“+”来拼接字符串是什么实现的?
如上面的代码,首先创建一个 String
对象 a
,再把“abc”赋值给它,后面Java虚拟机又创建了一个 String
对象 a
,然后再把原来的 a
的值和 “d” 加起来再赋值给新的 a
,而原来的a
就会被Java虚拟机的垃圾回收机制(GC)给回收掉了,所以,a
实际上并没有被更改,也就是前面说的 String
对象一旦创建之后就不可更改了。从这里可以看出,对于频繁操作的字符串,不建议使用 String
类型,这将会是一个不断创建新的对象并且将旧的对象回收的一个过程,所以执行速度很慢。
对于 String
类型对象的“+”操作,通过在 StringBuilder
的 append 方法上面打断点,可以发现对于String的操作,其实是使用了 StringBuilder
的 append
操作,这个不是线程安全。详细可以看下面关于 StringBuilder
的源码。
String
的性能差,主要是对于频繁操作的字符串,不断的创建和销毁对象,对于程序性能有很大的影响。
StringBuffer
从源码中我们可以看出,对 StringBuffer
来说,底层也是 char 数组。StringBuffer
默认初始空间是16。对于 StringBuffer
的扩容,从下面的这张图上面可以看出,是在旧的数组的2倍上面,再加2进行扩容。
下面我们在来看下 StringBuffer
的操作函数append
,append
方法是由 synchronized
修饰的,是线程安全的。
StringBuilder
通过和 StringBuffer
的源码比较,我们发现,底层也是char数组,初始空间也是16。StringBuilder
和 StringBuffer
都是从 AbstractStringBuilder
继承来的,所以对于其初始空间和扩容都是相同的。
对于 StringBuilder
和 StringBuffer
的区别可以从下面的这张图片上看出,对于append()方法,缺少了synchronized
修饰,这使得 StringBuilder
不是一个线程安全。
三者性能比较
我做了个测试,代码如下:
1 | public static void main(String[] args) { |
再来看下运行的结果:
1 | 405 |
从运行结果,我们可以非常明显的看出这三者的性能比较。
总结
String 长度大小不可变
StringBuffer 和 StringBuilder 长度可变
StringBuffer 线程安全 StringBuilder 线程不安全
因此:
String:适用于少量的字符串操作的情况
StringBuilder:适用于单线程下在字符缓冲区进行大量操作的情况
StringBuffer:适用多线程下在字符缓冲区进行大量操作的情况