Java String、StringBuffer、StringBuilder深度剖析
2016-04-28 12:17
615 查看
三种字符串构造方式
String方式String testString = "a"; for (int i = 0; i < 10; i++) { testString += "b"; }
StringBuffer方式
StringBuffer sbuf = new StringBuffer(); for (int i = 0; i < 10; i++) { sbuf.append("b"); }
StringBuilder方式
StringBuilder sbud = new StringBuilder(); for (int i = 0; i < 10; i++) { sbud.append("b"); }
三种方案剖析
利用String的 a+=”b”这种方式来构造字符串在我们平时用的比较多,用起来也很方便,但是总有一些有开发经验的前辈们会说:“尽量少使用这种方式,应该多使用StringBuffer、StringBuilder等方式来构造,这种方式会降低整个程序的性能!”,但是至于为什么使用这种方式会降低程序的性能,我们无从得知。今天我编写这篇博客的目的就是对这三种方式进行深入剖析,让读者真正的了解为什么!1. String构造方式解析
我们常常会使用
String testString = "a"; testString += "b";这种方式来构造字符串,但是却被告知这种方式会降低性能,这到底是怎么一回事呢?接下来我会通过分析源码的方式给大家仔细分析为什么!
String.java
// 为什么说String不是基本数据类型的原因也可以从这段代码中看出来 // String字符串的实现方式是建立在字符数组之上的 public final class String implements java.io.Serializable, Comparable<String>, CharSequence { /** The value is used for character storage. */ /** String的内容是存放在字符数组中的. final意味着字符串的内容是只读的,不可修改的*/ private final char value[]; .... }
大家从这段代码中看到了什么?final关键字,这意味着String从一开始初始化,它的内容是不允许修改的,那么我们平时通过
String testString = "a"; testString = "b";这种方式对字符串重新赋值是怎么做到的呢,既然我们不能修改String的值,那就只能重新创建一个String对象了,然后让testString指向新的String对象的地址即可,这就意味着每一次执行赋值(testString=“a”或者testString += “a”)都会重新创建一个对象,然而创建对象和销毁对象是一个浪费时间和空间的过程,所以有过经验的开发者都会说,尽量不要使用这种方式构造字符串,这也是为什么说这种方式会影响程序性能的原因了。
2. StringBuffer方式的构造
AbstractStringBuilder.java
abstract class AbstractStringBuilder implements Appendable, CharSequence { /** * The value is used for character storage. */ /* 存放字符串的字符数组 */ char[] value; ...... public AbstractStringBuilder append(String str) { if (str == null) return appendNull(); int len = str.length(); ensureCapacityInternal(count + len); str.getChars(0, len, value, count); count += len; return this; } }
StringBuffer.java
// 继承AbstractStringBuilder,所以说也继承了char[] value; public final class StringBuffer extends AbstractStringBuilder implements java.io.Serializable, CharSequence { /** * A cache of the last value returned by toString. Cleared * whenever the StringBuffer is modified. */ /* 缓存上一次toString方法返回的值,每次append或者其他修改操作的时候都会清空*/ private transient char[] toStringCache; ...... public synchronized int offsetByCodePoints(int index, int codePointOffset) { return super.offsetByCodePoints(index, codePointOffset); } @Override public synchronized StringBuffer append(Object obj) { toStringCache = null; super.append(String.valueOf(obj)); return this; } @Override public synchronized StringBuffer append(String str) { toStringCache = null; super.append(str); return this; } ...... @Override public synchronized String toString() { // 生成String对象 if (toStringCache == null) { toStringCache = Arrays.copyOfRange(value, 0, count); } return new String(toStringCache, true); }
大家从上面的代码有没有发现什么?synchronized关键字、char[] value已经没有用final修饰了。synchronized修饰意味着这是线程安全的,适合多线程使用;value不再使用final修饰意味着每次insert、append只是纯粹的修改了value的值,而没有重新创建新的String对象,减少了对象创建和销毁的过程,更高效。
总结一下两点
1. synchronized修饰,可以多线程使用,但是锁的争用降低效率,如果单线程想要使用此方法构造,同样会存在上锁和释放锁的过程,效率低下。
2. char[] value已经没有用final修饰了,字符串的构造过程只是值的修改过程,没有对象的创建,效率更高。
3. StringBuilder构造方式的剖析
AbstractStringBuilder.java
// 和StringBuffer一样的父类
abstract class AbstractStringBuilder implements Appendable, CharSequence { /** * The value is used for character storage. */ /* 存放字符串的字符数组 */ char[] value; ...... public AbstractStringBuilder append(String str) { if (str == null) return appendNull(); int len = str.length(); ensureCapacityInternal(count + len); str.getChars(0, len, value, count); count += len; return this; } }
StringBuilder.java
// 和StringBuffer一样继承AbstractStringBuilder // 字符串存放在AbstractStringBuilder的value中 public final class StringBuilder extends AbstractStringBuilder implements java.io.Serializable, CharSequence { ...... @Override public StringBuilder append(Object obj) { return append(String.valueOf(obj)); } @Override public StringBuilder append(String str) { super.append(str); return this; } ...... @Override public String toString() { // Create a copy, don't share the array return new String(value, 0, count); } }
大家可以看到方法修饰少了synchronized关键字,少了toStringCache(并没有什么特别的影响),除此之外和StringBuffer是差不多的。少了synchronized更方便单线程使用,提高了效率。
总结
通过上面我们分别对String、StringBuffer、StringBuilder的源码分析,总结三种方式进行字符串构造的优缺点如下:1. String字符串构造方式会不断的创建和销毁对象,时间和空间浪费大。
2. StringBuffer方式不会频繁的创建和销毁对象,性能大大提升,而且操作是多线程安全的,所以适合多线程的字符串构造,但是对于单线程来说仍然存在上锁和释放锁的过程,增加了构造的时间,不利于单线程操作。
3. StringBuilder大体上同StringBuffer一样,但是没有加入多线程的安全机制,不适合多线程使用,但是由于没有引入多线程安全的机制,所以去除了上锁和释放锁的过程,减少了构造的时间,适合单线程使用。
相关文章推荐
- PYQT4 UI 线程分离
- UI测试导入Espresso时的冲突
- 62. Unique Paths && 63. Unique Paths II
- UILabel显示html文本
- MenuItem创建注意事项
- iOS9中UIDatePicker的用法
- <OJ_Sicily>Hanoi_Tower_Sequence
- 数据库中创建unique唯一约束
- String,StringBuffer与StringBuilder的区别
- Android蓝牙4.0API-类-BluetoothGattCharacteristic
- POJ 2299 Ultra-QuickSort(逆序数)
- iOS 之UIButton 使用详解
- Android蓝牙4.0API-类-BluetoothGattCallback
- iOS 之 UITableView 使用详解/性能优化/UITableViewCell/UITableViewController
- SSh结合Easyui实现Datagrid的分页显示
- make 2>&1 tee build_log.txt
- iOS 之 UITableView 使用详解
- Android蓝牙4.0API-类-BluetoothGatt
- UIControl 的基本使用方法和 Target-Action 机制
- 2016.04.28,英语,《Vocabulary Builder》Unit 20