Java StringBuilder和StringBuffer源码分析

StringBuilder与StringBuffer是两个常用的操作字符串的类。大家都知道,StringBuilder是线程不安全的,而StringBuffer是线程安全的。前者是JDK1.5加入的,后者在JDK1.0就有了。下面分析一下它们的内部实现。

一、继承关系

?

1 2 3 4 5 6 7 public final class StringBuffer extends AbstractStringBuilder implements java.io.Serializable, CharSequence   public final class StringBuilder extends AbstractStringBuilder implements java.io.Serializable, CharSequence

可以看到,两个类的继承关系是一模一样的。Serializable是可以序列化的标志。CharSequence接口包含了charAt()、length() 、subSequence()、toString()这几个方法,String类也实现了这个接口。这里的重点是抽象类AbstractStringBuilder,这个类封装了StringBuilder和StringBuffer大部分操作的实现。

二、AbstractStringBuilder

1、变量及构造方法

?

1 2 3 4 5 6 7 8 char [] value; int count; AbstractStringBuilder() { } AbstractStringBuilder( int capacity) {    value = new char [capacity]; }   

AbstractStringBuilder内部用一个char[]数组保存字符串,可以在构造的时候指定初始容量方法。

2、扩容

?

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 public void ensureCapacity( int minimumCapacity) {    if (minimumCapacity > 0 )      ensureCapacityInternal(minimumCapacity); }   private void ensureCapacityInternal( int minimumCapacity) {    // overflow-conscious code    if (minimumCapacity - value.length > 0 )      expandCapacity(minimumCapacity); } void expandCapacity( int minimumCapacity) {    int newCapacity = value.length * 2 + 2 ;    if (newCapacity - minimumCapacity < 0 )      newCapacity = minimumCapacity;    if (newCapacity < 0 ) {      if (minimumCapacity < 0 ) // overflow        throw new OutOfMemoryError();      newCapacity = Integer.MAX_VALUE;    }    value = Arrays.copyOf(value, newCapacity); }

扩容的方法最终是由expandCapacity()实现的,在这个方法中首先把容量扩大为原来的容量加2,如果此时仍小于指定的容量,那么就把新的容量设为minimumCapacity。然后判断是否溢出,如果溢出了,把容量设为Integer.MAX_VALUE。最后把value值进行拷贝,这显然是耗时操作。

3、append()方法

?

1 2 3 4 5 6 7 8 9 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 ;    }

append()是最常用的方法,它有很多形式的重载。上面是其中一种,用于追加字符串。如果str是null,则会调用appendNull()方法。这个方法其实是追加了’n’、’u’、’l’、’l’这几个字符。如果不是null,则首先扩容,然后调用String的getChars()方法将str追加到value末尾。最后返回对象本身,所以append()可以连续调用。

三、StringBuilder

AbstractStringBuilder已经实现了大部分需要的方法,StringBuilder和StringBuffer只需要调用即可。下面来看看StringBuilder的实现。

1、构造器

?

1 2 3 4 5 6 7 8 9 10 11 12 13 14 public StringBuilder() {    super ( 16 ); } public StringBuilder( int capacity) {    super (capacity); } public StringBuilder(String str) {    super (str.length() + 16 );    append(str); } public StringBuilder(CharSequence seq) {    this (seq.length() + 16 );    append(seq); }

可以看出,StringBuilder默认的容量大小为16。当然也可以指定初始容量,或者以一个已有的字符序列给StringBuilder对象赋初始值。

2、append()方法

?

1 2 3 4 5 6 7 8 public StringBuilder append(String str) {    super .append(str);    return this ; } public StringBuilder append(CharSequence s) {    super .append(s);    return this ; }

append()的重载方法很多,这里随便列举了两个。显然,这里是直接调用的父类AbstractStringBuilder中的方法。

3、toString()

?

1 2 3 4 public String toString() {    // Create a copy, don't share the array    return new String(value, 0 , count); }

toString()方法返回了一个新的String对象,与原来的对象不共享内存。其实AbstractStringBuilder中的subString()方法也是如此。

四、SringBuffer

StiringBuffer跟StringBuilder类似,只不过为了实现同步,很多方法使用lSynchronized修饰,如下面的方法:

?

1 2 3 4 5 6 7 8 9 10 11 12 public synchronized int length() {      return count; } public synchronized StringBuffer append(String str) {    toStringCache = null ;    super .append(str);    return this ; } public synchronized void setLength( int newLength) {    toStringCache = null ;    super .setLength(newLength); }

可以看到,方法前面确实加了Synchronized。
另外,在上面的append()以及setLength()方法里面还有个变量toStringCache。这个变量是用于最近一次toString()方法的缓存,任何时候只要StringBuffer被修改了这个变量会被赋值为null。StringBuffer的toString如下:

?

1 2 3 4 5 6 public synchronized String toString() {    if (toStringCache == null ) {      toStringCache = Arrays.copyOfRange(value, 0 , count);    }    return new String(toStringCache, true ); }

在这个方法中,如果toStringCache为null则先缓存。最终返回的String对象有点不同,这个构造方法还有个参数true。找到String的源码看一下:

?

1 2 3 4 String( char [] value, boolean share) {    // assert share : "unshared not supported";    this .value = value; }

原来这个构造方法构造出来的String对象并没有实际复制字符串,只是把value指向了构造参数,这是为了节省复制元素的时间。不过这个构造器是具有包访问权限,一般情况下是不能调用的。

总结

  • StringBuilder和StringBuffer都是可变字符串,前者线程不安全,后者线程安全。
  • StringBuilder和StringBuffer的大部分方法均调用父类AbstractStringBuilder的实现。其扩容机制首先是把容量变为原来容量的2倍加2。最大容量是Integer.MAX_VALUE,也就是0x7fffffff。
  • StringBuilder和StringBuffer的默认容量都是16,最好预先估计好字符串的大小避免扩容带来的时间消耗。

以上就是本文的全部内容,希望对大家学习Java中两个常用的操作字符串的类StringBuilder和StringBuffer有所帮助。

    原文作者:天涯海角路
    原文地址: http://www.cnblogs.com/aademeng/articles/6193358.html
    本文转自网络文章,转载此文章仅为分享知识,如有侵权,请联系博主进行删除。
点赞