Android(java):StringBuffer StringBulider区别

来源:互联网 发布:mysql数据备份方案 编辑:程序博客网 时间:2024/05/16 05:48

java数组复制 System.copyarray()方法

System.copyarray()方法可以用来复制数组,其格式是:

System.arraycopy(Object src, int src_position, Object dst, int dst_position, int length)

它将数组从src 复制到dst,复制的位置是src 的第src_position 个元素到dst 的第dst_position

位置,复制元素的个数为length。

注意:该方法只复制元素。如果数组元素是引用型变量,则只复制引用,不复制对象

实体。

 
StringBuffer 字符串变量(线程安全)
StringBuilder 字符串变量(非线程安全)

String 类型和 StringBuffer 、StringBuilder 类型的主要性能区别其实在于 String 是不可变的对象,而后俩者都是可变的。

来看看 StringBuffer类源码定义:

Java代码 复制代码收藏代码
  1.   
  2. public finalclass StringBuffer 
  3.     extends AbstractStringBuilder 
  4.     implements java.io.Serializable, CharSequence 
  5.  
  6.     public StringBuffer() { 
  7.     super(16); 
  8.     } 
  9.  
  10.      public StringBuffer(int capacity) { 
  11.     super(capacity); 
  12.     } 
[java] view plaincopyprint?
  1. <STRONG>   
  2. public final class StringBuffer  
  3.     extends AbstractStringBuilder  
  4.     implements java.io.Serializable, CharSequence  
  5. {  
  6.   
  7.     public StringBuffer() {  
  8.     super(16);  
  9.     }  
  10.   
  11.      public StringBuffer(int capacity) {  
  12.     super(capacity);  
  13.     }  
  14. </STRONG>  
 public final class StringBuffer    extends AbstractStringBuilder    implements java.io.Serializable, CharSequence{    public StringBuffer() {super(16);    }     public StringBuffer(int capacity) {super(capacity);    }


上例代码我们发现 StringBuffer 继承了 AbstractStringBuilder抽象类。包括构造方法的实现,都是父类提供的。

然后,我们打开 StringBuilder源码定义:

Java代码 复制代码收藏代码
  1. public finalclass StringBuilder 
  2.     extends AbstractStringBuilder 
  3.     implements java.io.Serializable, CharSequence 
  4.  
  5.     public StringBuilder() { 
  6.     super(16); 
  7.     } 
  8.  
  9.     public StringBuilder(int capacity) { 
  10.     super(capacity); 
  11.     } 
[java] view plaincopyprint?
  1. <STRONG>public final class StringBuilder  
  2.     extends AbstractStringBuilder  
  3.     implements java.io.Serializable, CharSequence  
  4. {  
  5.   
  6.     public StringBuilder() {  
  7.     super(16);  
  8.     }  
  9.   
  10.     public StringBuilder(int capacity) {  
  11.     super(capacity);  
  12.     }  
  13. </STRONG>  
public final class StringBuilder    extends AbstractStringBuilder    implements java.io.Serializable, CharSequence{    public StringBuilder() {super(16);    }    public StringBuilder(int capacity) {super(capacity);    }

我们发现两者都继承了AbstractStringBuilder抽象类。且构造方法都调用父类实现。

我们接着看两者append方法实现:

先看StringBuffer的:

Java代码 复制代码收藏代码
  1.    
  2. public synchronized StringBuffer append(Object obj) { 
  3.        super.append(String.valueOf(obj)); 
  4.        return this
  5.    } 
  6.  
  7.    public synchronized StringBuffer append(String str) { 
  8.        super.append(str); 
  9.        return this
  10.    } 
  11.    //... 
[java] view plaincopyprint?
  1. <STRONG>     
  2.  public synchronized StringBuffer append(Object obj) {  
  3.         super.append(String.valueOf(obj));  
  4.         return this;  
  5.     }  
  6.   
  7.     public synchronized StringBuffer append(String str) {  
  8.         super.append(str);  
  9.         return this;  
  10.     }  
  11.     //...   
  12. </STRONG>  
    public synchronized StringBuffer append(Object obj) {        super.append(String.valueOf(obj));        return this;    }    public synchronized StringBuffer append(String str) {        super.append(str);        return this;    }    //...


再看StringBuilder的:
Java代码 复制代码收藏代码
  1. public StringBuilder append(Object obj) { 
  2.     return append(String.valueOf(obj)); 
  3.  
  4. public StringBuilder append(String str) { 
  5.     super.append(str); 
  6.     return this
  7. //... 
[java] view plaincopyprint?
  1. <STRONG>      
  2.     public StringBuilder append(Object obj) {  
  3.         return append(String.valueOf(obj));  
  4.     }  
  5.   
  6.     public StringBuilder append(String str) {  
  7.         super.append(str);  
  8.         return this;  
  9.     }  
  10.     //...   
  11. </STRONG>  
        public StringBuilder append(Object obj) {        return append(String.valueOf(obj));    }    public StringBuilder append(String str) {        super.append(str);        return this;    }    //...


对比上面两段源码 我们发现 StirngBuffer 和StringBuilder的 append实现都是调用父类实现的。唯一不同的是 StringBuffer是线程安全的,方法中多了synchronized ,而StringBuilder 是非线程安全的。

我们看下父类AbstractStringBuilder 定义 及 append 实现:

Java代码 复制代码收藏代码
  1. abstract class AbstractStringBuilderimplements Appendable, CharSequence { 
  2.     //底层与String类一样都是 char类型数组。 
  3.     char value[]; 
  4.     //字符串长度 
  5.     int count; 
  6.  
  7.     AbstractStringBuilder() { 
  8.     } 
  9.  
  10.     AbstractStringBuilder(int capacity) { 
  11.         value = new char[capacity]; 
  12.     } 
[java] view plaincopyprint?
  1. <STRONG>abstract class AbstractStringBuilder implements Appendable, CharSequence {  
  2.     //底层与String类一样都是 char类型数组。   
  3.     char value[];  
  4.     //字符串长度   
  5.     int count;  
  6.   
  7.     AbstractStringBuilder() {  
  8.     }  
  9.   
  10.     AbstractStringBuilder(int capacity) {  
  11.         value = new char[capacity];  
  12.     }  
  13. </STRONG>  
abstract class AbstractStringBuilder implements Appendable, CharSequence {    //底层与String类一样都是 char类型数组。    char value[];    //字符串长度    int count;    AbstractStringBuilder() {    }    AbstractStringBuilder(int capacity) {        value = new char[capacity];    }


Java代码 复制代码收藏代码
  1. public AbstractStringBuilder append(Object obj) { 
  2.     return append(String.valueOf(obj)); 
  3.  
  4. public AbstractStringBuilder append(String str) { 
  5.     //对于str==null的 
  6.     if (str == null) str ="null"
  7.     int len = str.length(); 
  8.     if (len == 0)returnthis
  9.     //新的字符串长度 
  10.     int newCount = count + len; 
  11.     if (newCount > value.length) 
  12.          //当新的字符串长度比原先数组长度大时,需要对char 数组扩容。 
  13.            expandCapacity(newCount); 
  14.     //将新添的数据追加到char类型数组中。 
  15.      str.getChars(0, len, value, count); 
  16.     count = newCount; 
  17.     return this
  18.  
  19. //数组扩容  
  20. void expandCapacity(int minimumCapacity) { 
  21.     //先扩容成 (原先的长度+1)*2 
  22.     int newCapacity = (value.length +1) *2
  23.     //判断newCapacity值是否满足要求 
  24.      //如果新的长度还是不够,则直接取值 minimumCapacity  
  25.     if (newCapacity < 0) { 
  26.        newCapacity = Integer.MAX_VALUE; 
  27.     } else if (minimumCapacity > newCapacity) { 
  28.        newCapacity = minimumCapacity; 
  29.     }    
  30.     char newValue[] = new char[newCapacity]; 
  31.     //将原先的数据拷贝到新的char 数组中。 
  32.      System.arraycopy(value, 0, newValue,0, count); 
  33.     value = newValue; 
  34.  
  35. public void getChars(int srcBegin,int srcEnd,char dst[],int dstBegin) { 
  36.     if (srcBegin < 0) { 
  37.         throw new StringIndexOutOfBoundsException(srcBegin); 
  38.     } 
  39.     if (srcEnd > count) { 
  40.         throw new StringIndexOutOfBoundsException(srcEnd); 
  41.     } 
  42.     if (srcBegin > srcEnd) { 
  43.         throw new StringIndexOutOfBoundsException(srcEnd - srcBegin); 
  44.     } 
  45.     System.arraycopy(value, offset + srcBegin, dst, dstBegin, 
  46.          srcEnd - srcBegin); 

StringBuffer与StringBuilder是java.lang包下被大家熟知的两个类。其异同为:一、长度都是可扩充的;二、StringBuffer是线程安全的,StringBuilder是线程不安全的。那么他们的长度是如何实现动态扩充以及StringBuffer的线程安全是如何实现的呢?通过“深度”阅读它们的源代码,最终弄明白其中的缘由。

正文

首先上一张StringBuffer和StringBuilder类结构图:


抽象类AbstractStringBuilder(也是核心实现类)实现了Appendable和CharSequence两个接口;StringBuffer与StringBuilder统统继承自AbstractStringBuilder,并且实现了java.io.Serializable和CharSequence接口。

下面简单描述下这几个接口所起到的作用(引用自中文api)。

  • Appendable:能够被添加 char 序列和值的对象。如果某个类的实例打算接收java.util.Formatter 的格式化输出,那么该类必须实现 Appendable 接口
    。要添加的字符应该是有效的 Unicode 字符,正如 Unicode Character Representation 中描述的那样。注意,增补字符可能由多个 16 位char 值组成
  • CharSequence:CharSequence 是 char 值的一个可读序列。此接口对许多不同种类的 char 序列提供统一的只读访问。char 值表示 Basic Multilingual Plane (BMP) 或代理项中的一个字符。有关详细信息,请参阅Unicode 字符表示形式。此接口不修改equalshashCode 方法的常规协定。因此,通常未定义比较实现 CharSequence 的两个对象的结果。每个对象都可以通过一个不同的类实现,而且不能保证每个类能够测试其实例与其他类的实例的相等性。因此,使用任意 CharSequence 实例作为集合中的元素或映射中的键是不合适的。
  • Serializable:类通过实现 java.io.Serializable 接口以启用其序列化功能。未实现此接口的类将无法使其任何状态序列化或反序列化。可序列化类的所有子类型本身都是可序列化的。序列化接口没有方法或字段,仅用于标识可序列化的语义。
  • AbstractStringBuilde
    r这个抽象类提供了StringBuffer和StringBuilder绝大部分的实现。在AbstractStringBuilder的描述中说:如果去掉线程安全,那么StringBuffer和StringBuilder是完全一致的。从实现的角度来说,StringBuffer所有方法(构造方法除外,因为没有必要)签名中都使用synchronized限定,也就是所有的方法都是同步的。

eg.StringBuffer中replace()方法

view sourceprint?
1public synchronized StringBuffer replace(intstart,int end, String str) {
2super.replace(start, end, str);
3return this;
4}

StringBuilder中replace():

view sourceprint?
1public StringBuilder replace(intstart,int end, String str) {
2super.replace(start, end, str);
3return this;
4}

区别仅仅在方法签名上是否有synchronized。

另外需要稍稍注意的问题是:StringBuffer同步只同步目标,比如:sb.append("i am not synchronized"),sb是同步的,而其中的参数未必是同步的。

而它们两个可扩展长度则是通过ensureCapacity(int minimumCapacity)来验证当前长度是否小于参数minimumCapacity,如果成立则进行分配空间。分配新空间的步长为(当前长度+1)的两倍。

实现如下:

view sourceprint?
01public void ensureCapacity(intminimumCapacity) {
02if (minimumCapacity > value.length) {
03expandCapacity(minimumCapacity);
04}
05}
06void expandCapacity(int minimumCapacity) {
07int newCapacity = (value.length + 1) *2;
08if (newCapacity < 0) {
09newCapacity = Integer.MAX_VALUE;
10} else if (minimumCapacity > newCapacity) {
11newCapacity = minimumCapacity;
12}
13value = Arrays.copyOf(value, newCapacity);
14}

如果新的长度小于0(溢出了),则使用Integer的最大值作为长度。

另外,在阅读源码的过程中,发现两个有趣的问题,下面一一道来。

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

上源码,再做解释:

view sourceprint?
01public AbstractStringBuilder reverse() {
02boolean hasSurrogate = false;
03int n = count - 1;
04for (int j = (n-1) >>1; j >=0; --j) {
05char temp = value[j];
06char temp2 = value[n - j];
07if (!hasSurrogate) {
08hasSurrogate = (temp >= Character.MIN_SURROGATE && temp <= Character.MAX_SURROGATE)
09|| (temp2 >= Character.MIN_SURROGATE && temp2 <= Character.MAX_SURROGATE);
10}
11value[j] = temp2;
12value[n - j] = temp;
13}
14if (hasSurrogate) {
15// Reverse back all valid surrogate pairs
16for (int i = 0; i < count -1; i++) {
17char c2 = value[i];
18if (Character.isLowSurrogate(c2)) {
19char c1 = value[i + 1];
20if (Character.isHighSurrogate(c1)) {
21value[i++] = c1;
22value[i] = c2;
23}
24}
25}
26}
27return this;
28}

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

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

 

[java] view plaincopyprint?
  1. <STRONG>      
  2.     public AbstractStringBuilder append(Object obj) {  
  3.         return append(String.valueOf(obj));  
  4.     }  
  5.       
  6.     public AbstractStringBuilder append(String str) {  
  7.         //对于str==null的   
  8.         if (str == null) str = "null";  
  9.         int len = str.length();  
  10.         if (len == 0return this;  
  11.         //新的字符串长度   
  12.         int newCount = count + len;  
  13.         if (newCount > value.length)  
  14.              //当新的字符串长度比原先数组长度大时,需要对char 数组扩容。  
  15.                expandCapacity(newCount);  
  16.         //将新添的数据追加到char类型数组中。   
  17.          str.getChars(0, len, value, count);  
  18.         count = newCount;  
  19.         return this;  
  20.     }  
  21.       
  22.     //数组扩容    
  23.     void expandCapacity(int minimumCapacity) {  
  24.         //先扩容成 (原先的长度+1)*2   
  25.         int newCapacity = (value.length + 1) * 2;  
  26.         //判断newCapacity值是否满足要求   
  27.          //如果新的长度还是不够,则直接取值 minimumCapacity   
  28.         if (newCapacity < 0) {  
  29.            newCapacity = Integer.MAX_VALUE;  
  30.         } else if (minimumCapacity > newCapacity) {  
  31.            newCapacity = minimumCapacity;  
  32.         }     
  33.         char newValue[] = new char[newCapacity];  
  34.         //将原先的数据拷贝到新的char 数组中。   
  35.          System.arraycopy(value, 0, newValue, 0, count);  
  36.         value = newValue;  
  37.     }  
  38.   
  39.      public void getChars(int srcBegin, int srcEnd, char dst[], int dstBegin) {  
  40.         if (srcBegin < 0) {  
  41.             throw new StringIndexOutOfBoundsException(srcBegin);  
  42.         }  
  43.         if (srcEnd > count) {  
  44.             throw new StringIndexOutOfBoundsException(srcEnd);  
  45.         }  
  46.         if (srcBegin > srcEnd) {  
  47.             throw new StringIndexOutOfBoundsException(srcEnd - srcBegin);  
  48.         }  
  49.         System.arraycopy(value, offset + srcBegin, dst, dstBegin,  
  50.              srcEnd - srcBegin);  
  51.     }  
  52. </STRONG>  
        public AbstractStringBuilder append(Object obj) {        return append(String.valueOf(obj));    }        public AbstractStringBuilder append(String str) {        //对于str==null的        if (str == null) str = "null";        int len = str.length();        if (len == 0) return this;        //新的字符串长度        int newCount = count + len;        if (newCount > value.length)             //当新的字符串长度比原先数组长度大时,需要对char 数组扩容。               expandCapacity(newCount);        //将新添的数据追加到char类型数组中。         str.getChars(0, len, value, count);        count = newCount;        return this;    }        //数组扩容     void expandCapacity(int minimumCapacity) {        //先扩容成 (原先的长度+1)*2        int newCapacity = (value.length + 1) * 2;        //判断newCapacity值是否满足要求         //如果新的长度还是不够,则直接取值 minimumCapacity         if (newCapacity < 0) {           newCapacity = Integer.MAX_VALUE;        } else if (minimumCapacity > newCapacity) {           newCapacity = minimumCapacity;        }        char newValue[] = new char[newCapacity];        //将原先的数据拷贝到新的char 数组中。         System.arraycopy(value, 0, newValue, 0, count);        value = newValue;    }     public void getChars(int srcBegin, int srcEnd, char dst[], int dstBegin) {        if (srcBegin < 0) {            throw new StringIndexOutOfBoundsException(srcBegin);        }        if (srcEnd > count) {            throw new StringIndexOutOfBoundsException(srcEnd);        }        if (srcBegin > srcEnd) {            throw new StringIndexOutOfBoundsException(srcEnd - srcBegin);        }        System.arraycopy(value, offset + srcBegin, dst, dstBegin,             srcEnd - srcBegin);    }