String、StringBuffer和StringBuilder


阅读java源码的一些体会


一、CharSequence接口

public interface CharSequence {
     int length();
     char charAt(int index);
     CharSequence subSequence(int start, int end);
     public String toString();
     public default IntStream chars() {……}
     public default IntStream codePoints() {……}
}

   default是java8的新特性,具体可以想见文档,显然此接口主要是保证有获取内容的公共接口。还实现了转换为IntStream的默认函数。

二、String类的一些感觉

1.结构

  • 请看类的实现:
public final class String
    implements java.io.Serializable, Comparable, CharSequence{
    private fianl char value[];
    private int hash;
    private static final long serialVersionUID = -6849794470754667710L;
    private static final ObjectStreamField[] serialPersistentFields =
        new ObjectStreamField[0];
  /**省略实现细节
**/
}

   可以看到此时的结构体一共有四个变量。扣除为了实现进程间传递的变量。主要的变量就是存储内容value和hash。其中value为其取值,而hash则是String的hash函数值。
   这里可以看到String类型中的value变量是final类型的,是不可变的变量,同时String类型也没有提供修改char[]数组的接口。这就可以肯定value在构造之初之后就不能改变。这表示新的内容产生新的对象。

2.常用代码分析

 public void getChars(int srcBegin, int srcEnd, char dst[], int dstBegin) {
        if (srcBegin < 0) {
            throw new StringIndexOutOfBoundsException(srcBegin);
        }
        if (srcEnd > value.length) {
            throw new StringIndexOutOfBoundsException(srcEnd);
        }
        if (srcBegin > srcEnd) {
            throw new StringIndexOutOfBoundsException(srcEnd - srcBegin);
        }
        //将内容从value中复制到dst数组中,arraycopy是native方法,这个方法显
        //然可以有相当强大的效率
        System.arraycopy(value, srcBegin, dst, dstBegin, srcEnd - srcBegin);
    }
 public int compareTo(String anotherString) {
        int len1 = value.length;
        int len2 = anotherString.value.length;
        int lim = Math.min(len1, len2);
        char v1[] = value;
        char v2[] = anotherString.value;
        int k = 0;
        while (k < lim) {
            char c1 = v1[k];
            char c2 = v2[k];
            if (c1 != c2) {
                return c1 - c2;
            }
            k++;
        }
        return len1 - len2;
    }
   public String substring(int beginIndex, int endIndex) {
        if (beginIndex < 0) {
            throw new StringIndexOutOfBoundsException(beginIndex);
        }
        if (endIndex > value.length) {
            throw new StringIndexOutOfBoundsException(endIndex);
        }
        int subLen = endIndex - beginIndex;
        if (subLen < 0) {
            throw new StringIndexOutOfBoundsException(subLen);
        }
      //产生了新的对象哦
        return ((beginIndex == 0) && (endIndex == value.length)) ? this
                : new String(value, beginIndex, subLen);
    }
 public String trim() {
        int len = value.length;
        int st = 0;
        char[] val = value;    /* avoid getfield opcode */

        while ((st < len) && (val[st] <= ' ')) {
            st++;
        }
        while ((st < len) && (val[len - 1] <= ' ')) {
            len--;
        }
//还是产生了新对象哦
        return ((st > 0) || (len < value.length)) ? substring(st, len) : this;
    }

   此外,replace、splite都和Pattern和matcher有关,将数组内容替换,并返回新的对象。

二、StringBuilder

1.结构

  • 请看类的实现:
public final class StringBuilder
    extends AbstractStringBuilder
    implements java.io.Serializable, CharSequence{
 /** use serialVersionUID for interoperability */
    static final long serialVersionUID = 4383685877147921099L;
    …………
 @Override
    public StringBuilder append(String str) {
        super.append(str);
        return this;
    }
}

   这里可以看到并没有其他的变量,那么我们看其父类:

abstract class AbstractStringBuilder implements Appendable, CharSequence {
  /**
     * The value is used for character storage.
     */
    char[] value;

    /**
     * The count is the number of characters used.
     */
    int count;
   /**
     * Creates an AbstractStringBuilder of the specified capacity.
     */
    AbstractStringBuilder(int capacity) {
        value = new char[capacity];
    }
  public AbstractStringBuilder append(Object obj) {
        return append(String.valueOf(obj));
    }
  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;
    }
  …………
}

   其中这里有一段关于vlaue数组长度扩展的关键代码:

  /**
     * This implements the expansion semantics of ensureCapacity with no
     * size check or synchronization.
     */
    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);
    }

   综上所述,我们可以看到,StringBuilder直接调用AbstractStringBuilder的父接口,其append每次首先检查char[]数组长度,如果大小不够,就重新扩充大小,并拷贝原来的数组内容,可以说StringBuilder是缓存的String,因此效率更高。想reaplace、delete这些方法都是在缓存数组上操作,然后返回对象本身。但如果需要获得String类型,则还是会返回String类型的对象。

三、StringBuffer

1.结构

  • 请看类的实现(通过我的read,方法都有synchronized,是线程安全得):

 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.
    * 关键字代表此变量之存在于内存中,而不能传输(例如持久化)
     */
    private transient char[] toStringCache;

    /** use serialVersionUID from JDK 1.0.2 for interoperability */
    static final long serialVersionUID = 3388685877147921107L;
    public StringBuffer(String str) {
        super(str.length() + 16);
        append(str);
    }
…………
 @Override
    public synchronized void ensureCapacity(int minimumCapacity) {
        if (minimumCapacity > value.length) {
            expandCapacity(minimumCapacity);
        }
    }
}

2.关键代码

  /**
     * @throws IndexOutOfBoundsException {@inheritDoc}
     * @see        #length()
     */
    @Override
    public synchronized void setCharAt(int index, char ch) {
        if ((index < 0) || (index >= count))
            throw new StringIndexOutOfBoundsException(index);
        toStringCache = null;
        value[index] = ch;
    }
    @Override
    public synchronized void setCharAt(int index, char ch) {
        if ((index < 0) || (index >= count))
            throw new StringIndexOutOfBoundsException(index);
        toStringCache = null;
        value[index] = ch;
    }
     @Override
      public synchronized StringBuffer append(String str) {
        toStringCache = null;
        super.append(str);
        return this;
    }
  @Override
    public synchronized StringBuffer insert(int index, char[] str, int offset,
                                            int len)
    {
        toStringCache = null;
        super.insert(index, str, offset, len);
        return this;
    }
   @Override
    public synchronized String toString() {
        if (toStringCache == null) {
            toStringCache = Arrays.copyOfRange(value, 0, count);
        }
        return new String(toStringCache, true);
    }

   可以说StringBuffer是线程安全得,但是其他的核心代码几乎是StringBuilder的拷贝。显然StringBuilder效率上要更好,然而多线程下显然是有危险的。

四、总结

    StringBuffer和StringBuilder异同:

  • StringBuffer是线程安全得可变序列,而StringBuilder是5.0新增的一个特性,用于和StringBuffer做兼容,其不保证线程安全,可以看到StringBuilder显然要比StringBuffer快。
  • 为了保证快速的toString,StringBuffer提供了字符缓存数组,显然这时候非常快。
  • String类每次改变都将产生新对象。因而StringBuffer一般情况下要比String快。
  • System.arraycopy是native方法,显然拷贝效率要更好,如果需要拷贝,可以 优先考虑使用arraycopy拷贝普通数组。
  • default关键字主要用于扩展接口的同时避免修改集成树,可以提供接口的默认实现,transient保证变量在持久化的过程中智能待在内存。

你可能感兴趣的:(String、StringBuffer和StringBuilder)