CharSequence,String ,Stringbuilder和StringBuffer源码分析

1、从类的定义看CharSequence、StringBuffer、StringBuilder、String的关系

下面先贴上这四者的定义(来自JDK1.6)

CharSequence是一个定义字符串操作的接口,StringBuffer、StringBuilder、String中都实现了这个接口.

复制代码
//CharSequence定义
public interface CharSequence


//StringBuffer定义
 public final class StringBuffer
    extends AbstractStringBuilder
    implements java.io.Serializable, CharSequence

//StringBuilder定义
public final class StringBuilder
    extends AbstractStringBuilder
    implements java.io.Serializable, CharSequence

//String定义
public final class String
    implements
java.io.Serializable, Comparable<String>, CharSequence
复制代码

String 是java中的字符串,它继承于CharSequence。 
String类所包含的API接口非常多。为了便于今后的使用,我对String的API进行了分类,并都给出的演示程序。

String 和 CharSequence 关系 
String 继承于CharSequence,也就是说String也是CharSequence类型。 
CharSequence是一个接口,它只包括length(), charAt(int index), subSequence(int start, int end)这几个API接口。除了String实现了CharSequence之外,StringBuffer和StringBuilder也实现了CharSequence接口。 
       也就是说,CharSequence其实也就是定义了字符串操作的接口,其他具体的实现是由String、StringBuilder、StringBuffer完成的,String、StringBuilder、StringBuffer都可以转化为CharSequence类型。

StringBuilder 和 StringBuffer 的区别

StringBuilder 和 StringBuffer都是可变的字符序列。它们都继承于AbstractStringBuilder,实现了CharSequence接口。 
但是,StringBuilder是非线程安全的,而StringBuffer是线程安全的。

它们之间的关系图如下: 

 

 

2、从构造函数到具体的字符串拼接操作看看String、StringBuffer、StringBuilder的区别

下面我们来分析一下String、StringBuffer、StringBuilder具体的构造函数,了解他们是怎么构造出来的,再看看具体的字符串连接操作。

(1)String

String的构造函数(几个常见的构造函数)

复制代码
public String() {
        this.offset = 0;
        this.count = 0;
        this.value = new char[0];
    }

    /**
     * Initializes a newly created {@code String} object so that it represents
     * the same sequence of characters as the argument; in other words, the
     * newly created string is a copy of the argument string. Unless an
     * explicit copy of {@code original} is needed, use of this constructor is
     * unnecessary since Strings are immutable.
     *
     * @param  original
     *         A {@code String}
     */
    public String(String original) {
        int size = original.count;
        char[] originalValue = original.value;
        char[] v;

        if (originalValue.length > size) {
            // The array representing the String is bigger than the new
            // String itself.  Perhaps this constructor is being called
            // in order to trim the baggage, so make a copy of the array.
            int off = original.offset;
            v = Arrays.copyOfRange(originalValue, off, off + size);
        } else {
            // The array representing the String is the same
            // size as the String, so no point in making a copy.
            v = originalValue;
        }

        this.offset = 0;
        this.count = size;
        this.value = v;
    }

    /**
     * Allocates a new {@code String} so that it represents the sequence of
     * characters currently contained in the character array argument. The
     * contents of the character array are copied; subsequent modification of
     * the character array does not affect the newly created string.
     *
     * @param  value
     *         The initial value of the string
     */
    public String(char[] value) {
        this.offset = 0;
        this.count = value.length;
        this.value = StringValue.from(value);
    }
复制代码

再看看String中具体的Concat函数

复制代码
public String concat(String str) {
        int otherLen = str.length();

        if (otherLen == 0) {
            return this;
        }

        char[] buf = new char[count + otherLen];
        getChars(0, count, buf, 0);
        str.getChars(0, otherLen, buf, count);

        return new String(0, count + otherLen, buf);
    }
复制代码

从Concat函数中,我们可以知道在对字符串使用concat操作后,具体的操作new出一个等同于两个字符串连接总长度的新的char数组,然后将两个字符串复制到新的char数组中,然后返回一个新的String对象。

(2)StringBuilder

StringBuilder常见构造函数

复制代码
public StringBuffer() {
    super(16);
    }


    public StringBuffer(int capacity) {
    super(capacity);
    }
复制代码

从StringBuilder的构造函数中,我们可以看见StringBuilder直接调用父类(AbstractStringBuilder)的构造函数,我们再看看AbstractStringBuilder的构造函数

复制代码
abstract class AbstractStringBuilder implements Appendable, CharSequence {
    final static int[] sizeTable = {
            9, 99, 999, 9999, 99999, 999999, 9999999, 99999999, 999999999,
            Integer.MAX_VALUE
        };

    /**
     * The value is used for character storage.
     */
    char[] value;

    /**
     * The count is the number of characters used.
     */
    int count;

    /**
     * This no-arg constructor is necessary for serialization of subclasses.
     */
    AbstractStringBuilder() {
    }

    /**
     * Creates an AbstractStringBuilder of the specified capacity.
     */
    AbstractStringBuilder(int capacity) {
        value = new char[capacity];
    }
//其他的一些逻辑
}
复制代码

从AbstractStringBuilder的构造函数中,我们可以看出StringBuilder中存储字符串其实用的是一个char数组,capacity其实就是指定这个char数组的大小。

下面我们再从StringBuilder中的append函数看看他具体是怎么做的(以 append(String str) 为例看看)。

public StringBuilder append(String str) {
        super.append(str);
        return this;
    }

又是直接调用父类(AbstractStringBuilder)的append方法,再跟到父类中去看看。

复制代码
/**
     *  value 用来存储字符串.
     */
    char value[];

    /** 
     * 有效字符串的数目.
     */
    int count;


    public AbstractStringBuilder append(String str) {
        if (str == null) {
            str = "null";
        }

        int len = str.length();
        if (len == 0) {
            return this;
        }

        int newCount = count + len;
        if (newCount > value.length) {
            expandCapacity(newCount);
        }
       //getChars将字符串复制到指定的位置
        str.getChars(0, len, value, count);
        count = newCount;
        return this;
    }
复制代码

上面的逻辑还是比较简单的,在append(str)函数调用的时候,首先会判断原来用于存储字符串的values的字符串数组有没有足够的大小来存储将要新添加入StringBuilder的字符串。如果不够用,那么就调用expandCapacity(int minimumCapacity)让容量翻两倍(一般是扩大两倍,特殊情况见代码),如果够用,那么就直接添加进去。

复制代码
/**
     * This implements the expansion semantics of ensureCapacity with no
     * size check or synchronization.
     */
    void expandCapacity(int minimumCapacity) {
        int newCapacity = (value.length + 1) * 2;

        if (newCapacity < 0) {
            newCapacity = Integer.MAX_VALUE;
        } else if (minimumCapacity > newCapacity) {
            newCapacity = minimumCapacity;
        }

        value = Arrays.copyOf(value, newCapacity);
    }
复制代码

(3)StringBuffer

StringBuffer的构造函数

复制代码
/**
     * Constructs a string buffer with no characters in it and an 
     * initial capacity of 16 characters. 
     */
    public StringBuffer() {
    super(16);
    }

    /**
     * Constructs a string buffer with no characters in it and 
     * the specified initial capacity. 
     *
     * @param      capacity  the initial capacity.
     * @exception  NegativeArraySizeException  if the <code>capacity</code>
     *               argument is less than <code>0</code>.
     */
    public StringBuffer(int capacity) {
    super(capacity);
    }
复制代码

StringBuffer也是直接调用父类(AbstractStringBuilder)的构造函数,那么我们从上面的分析中,就可以知道StringBuffer其实也是利用char[]类型的数组来保存字符串数组的。

再看看StringBuffer的append函数

public synchronized StringBuffer append(String str) {
    super.append(str);
        return this;
    }

还是调用父类的append函数,但是在这里有值得注意的地方,StringBuffer的append函数有一个synchronized标识符,也就是说StringBuffer中的append函数是线程安全的,通过继续查阅其他StringBuffer中的函数,我们也可以发现他们有synchronized标识符,这就不难理解为什么StringBuffer是线程安全的,但是很明显加上线程控制会拖慢程序运行的速度,所以如果不需要线程控制,那么最好就用StringBuilder。

复制代码
//下面只是节选一些StringBuffer中的函数
synchronized StringBuffer     append(char ch)
synchronized StringBuffer     append(char[] chars)
synchronized StringBuffer     append(char[] chars, int start, int length)
synchronized StringBuffer     append(Object obj)
synchronized StringBuffer     append(String string)
synchronized StringBuffer     append(StringBuffer sb)
synchronized StringBuffer     append(CharSequence s)
synchronized StringBuffer     append(CharSequence s, int start, int end)
synchronized StringBuffer     insert(int index, char ch)
synchronized StringBuffer     insert(int index, char[] chars)
synchronized StringBuffer     insert(int index, char[] chars, int start, int length)
synchronized StringBuffer     insert(int index, String string)
StringBuffer     insert(int index, Object obj)
虽然在源程序中使用了 "+" ,但在编译时仍然将 "+" 转换成 StringBuilder 。因此,我们可以得出结论, Java中无论使用何种方式进行字符串连接,实际上都使用的是StringBuilder
如果从运行结果来解释,那么 "+" StringBuilder 是完全等效的。但如果从运行效率和资源消耗方面看,那它们将存在很大的区别。

当然,如果连接字符串行表达式很简单(如上面的顺序结构),那么"+"StringBuilder基本是一样的,但如果结构比较复杂,如使用循环来连接字符串,那么产生的Java Byte Code就会有很大的区别。先让我们看看如下的代码:


  package string;
  
   import java.util.*;
  
   public  class TestComplexPlus
  {
       public  static  void main(String[] args)
      {
          String s = "";
          Random rand =  new Random();
           for ( int i = 0; i < 10; i++)
          {
              s = s + rand.nextInt(1000) + " ";
          }
          System.out.println(s);
      }
  }

      上面的代码返编译后的 Java Byte Code 如下:


package string;

import java.io.PrintStream;
import java.util.Random;

public  class TestComplexPlus
{

     public TestComplexPlus()
    {
     //     0    0:aload_0         
    
//     1    1:invokespecial   #8   <Method void Object()>
    
//     2    4:return          
    }

     public  static  void main(String args[])
    {
        String s = "";
     //     0    0:ldc1            #16  <String "">
    
//     1    2:astore_1        
        Random rand =  new Random();
     //     2    3:new             #18  <Class Random>
    
//     3    6:dup             
    
//     4    7:invokespecial   #20  <Method void Random()>
    
//     5   10:astore_2        
         for( int i = 0; i < 10; i++)
     // *   6   11:iconst_0        
    
// *   7   12:istore_3        
    
// *   8   13:goto            49
         s = ( new StringBuilder(String.valueOf(s))).append(rand.nextInt(1000)).append(" ").toString();
     //     9   16:new             #21  <Class StringBuilder>
    
//    10   19:dup             
    
//    11   20:aload_1         
    
//    12   21:invokestatic    #23  <Method String String.valueOf(Object)>
    
//    13   24:invokespecial   #29  <Method void StringBuilder(String)>
    
//    14   27:aload_2         
    
//    15   28:sipush          1000
    
//    16   31:invokevirtual   #32  <Method int Random.nextInt(int)>
    
//    17   34:invokevirtual   #36  <Method StringBuilder StringBuilder.append(int)>
    
//    18   37:ldc1            #40  <String " ">
    
//    19   39:invokevirtual   #42  <Method StringBuilder StringBuilder.append(String)>
    
//    20   42:invokevirtual   #45  <Method String StringBuilder.toString()>
    
//    21   45:astore_1        

    
//    22   46:iinc            3  1
    
//    23   49:iload_3         
    
//    24   50:bipush          10
    
//    25   52:icmplt          16
        System.out.println(s);
     //    26   55:getstatic       #49  <Field PrintStream System.out>
    
//    27   58:aload_1         
    
//    28   59:invokevirtual   #55  <Method void PrintStream.println(String)>
    
//    29   62:return          
    }
}

    大家可以看到,虽然编译器将 "+" 转换成了 StringBuilder ,但创建 StringBuilder 对象的位置却在 for 语句内部。这就意味着每执行一次循环,就会创建一个 StringBuilder 对象(对于本例来说,是创建了 10 StringBuilder 对象),虽然 Java 有垃圾回收器,但这个回收器的工作时间是不定的。如果不断产生这样的垃圾,那么仍然会占用大量的资源。解决这个问题的方法就是在程序中直接使用 StringBuilder 来连接字符串,代码如下:

package string;

import java.util.*;

public  class TestStringBuilder
{
     public  static  void main(String[] args)
    {
        String s = "";
        Random rand =  new Random();
        StringBuilder result =  new StringBuilder();
         for ( int i = 0; i < 10; i++)
        {
            result.append(rand.nextInt(1000));
            result.append(" ");
        }
        System.out.println(result.toString());
    }
}

上面代码反编译后的结果如下:


package string;

import java.io.PrintStream;
import java.util.Random;

public  class TestStringBuilder
{

     public TestStringBuilder()
    {
     //     0    0:aload_0         
    
//     1    1:invokespecial   #8   <Method void Object()>
    
//     2    4:return          
    }

     public  static  void main(String args[])
    {
        String s = "";
     //     0    0:ldc1            #16  <String "">
    
//     1    2:astore_1        
        Random rand =  new Random();
     //     2    3:new             #18  <Class Random>
    
//     3    6:dup             
    
//     4    7:invokespecial   #20  <Method void Random()>
    
//     5   10:astore_2        
        StringBuilder result =  new StringBuilder();
     //     6   11:new             #21  <Class StringBuilder>
    
//     7   14:dup             
    
//     8   15:invokespecial   #23  <Method void StringBuilder()>
    
//     9   18:astore_3        
         for( int i = 0; i < 10; i++)
     // *  10   19:iconst_0        
    
// *  11   20:istore          4
    
// *  12   22:goto            47
        {
            result.append(rand.nextInt(1000));
     //    13   25:aload_3         
    
//    14   26:aload_2         
    
//    15   27:sipush          1000
    
//    16   30:invokevirtual   #24  <Method int Random.nextInt(int)>
    
//    17   33:invokevirtual   #28  <Method StringBuilder StringBuilder.append(int)>
    
//    18   36:pop             
            result.append(" ");
     //    19   37:aload_3         
    
//    20   38:ldc1            #32  <String " ">
    
//    21   40:invokevirtual   #34  <Method StringBuilder StringBuilder.append(String)>
    
//    22   43:pop             
        }

     //    23   44:iinc            4  1
    
//    24   47:iload           4
    
//    25   49:bipush          10
    
//    26   51:icmplt          25
        System.out.println(result.toString());
     //    27   54:getstatic       #37  <Field PrintStream System.out>
    
//    28   57:aload_3         
    
//    29   58:invokevirtual   #43  <Method String StringBuilder.toString()>
    
//    30   61:invokevirtual   #47  <Method void PrintStream.println(String)>
    
//    31   64:return          
    }
}

从上面的反编译结果可以看出,创建StringBuilder的代码被放在了for语句外。虽然这样处理在源程序中看起来复杂,但却换来了更高的效率,同时消耗的资源也更少了。

在使用StringBuilder时要注意,尽量不要"+"StringBuilder混着用,否则会创建更多的StringBuilder对象,如下面代码所:

     for ( int i = 0; i < 10; i++)
    {
        result.append(rand.nextInt(1000));
        result.append(" ");
    }

改成如下形式:

for ( int i = 0; i < 10; i++)
{
     result.append(rand.nextInt(1000) + " ");
}

则反编译后的结果如下:


 
    for( int i = 0; i < 10; i++)
   // *  10   19:iconst_0        
  
// *  11   20:istore          4
  
// *  12   22:goto            65
   {
    result.append(( new StringBuilder(String.valueOf(rand.nextInt(1000)))).append(" ").toString());
   //    13   25:aload_3         
  
//    14   26:new             #21  <Class StringBuilder>
  
//    15   29:dup             
    

从上面的代码可以看出,Java编译器将"+"编译成了StringBuilder,这样for语句每循环一次,又创建了一个StringBuilder对象。
    如果将上面的代码在JDK1.4下编译,必须将StringBuilder改为StringBuffer,而JDK1.4"+"转换为StringBuffer(因为JDK1.4并没有提供StringBuilder类)。StringBufferStringBuilder的功能基本一样,只是StringBuffer是线程安全的,而StringBuilder不是线程安全的。因此,StringBuilder的效率会更高。

第一个,就是reverse的实现。其实我是第一次看StringBuilderStringBuffer的源码,这里面的reverse的实现是我所知道的java中的最高效的实现,没有之一。

上源码,再做解释:

 public AbstractStringBuilder reverse() {
	boolean hasSurrogate = false;
	int n = count - 1;
	for (int j = (n-1) >> 1; j >= 0; --j) {
	    char temp = value[j];
	    char temp2 = value[n - j];
	    if (!hasSurrogate) {
		hasSurrogate = (temp >= Character.MIN_SURROGATE && temp <= Character.MAX_SURROGATE)
		    || (temp2 >= Character.MIN_SURROGATE && temp2 <= Character.MAX_SURROGATE);
	    }
	    value[j] = temp2;
	    value[n - j] = temp;
	}
	if (hasSurrogate) {
	    // Reverse back all valid surrogate pairs
	    for (int i = 0; i < count - 1; i++) {
		char c2 = value[i];
		if (Character.isLowSurrogate(c2)) {
		    char c1 = value[i + 1];
		    if (Character.isHighSurrogate(c1)) {
			value[i++] = c1;
			value[i] = c2;
		    }
		}
	    }
	}
	return this;
    }


reverse分成两个部分:前面一个循环与后面的判断。

首先地一个循环很高效,循环次数为长度(count)的一半,而且使用>>位移运算,交换数组value[j]value[n-j]的值。这里一是循环次数少,而是使用最高效的位移运算所以说这个reverse很高效。在反转过程中还完成了一件事:就是为hasSurrogate赋值。赋值的依据就是value[j]value[n-j]两个字符时候有一个在\uD800\uDFFF之间,如果有则赋值为true

hasSurrogate的值作为下面一个if分支的依据,如果为true,则从头到尾循环一遍。至于为何要判断hasSurrogate,以及下面一个循环的意义,请移步这里:http://www.oschina.net/question/129471_37064 

其实到这里应该已经结束了,在我整理StringBufferStringBuilder结构图时发现(“刨祖坟”行家啊),发现它们两个又再次实现了CharSequence接口,为何说再次呢,因为AbstractStringBuilder已经实现了一次,不知何为!经过几个人讨论,结果还要请您再次移步这里:http://www.oschina.net/question/129471_37096 




 

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值