zoukankan      html  css  js  c++  java
  • JDK源码分析(8) StringBuffer & StringBuilder

    简介

    StringBufferStringBuilder是两个常用的操作字符串的类。大家都知道,StringBuilder是线程不安全的,而StringBuffer是线程安全的。前者是JDK1.5加入的,后者在JDK1.0就有了。

    继承关系

    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,这个类封装了StringBuilderStringBuffer大部分操作的实现。

    AbstractStringBuilder

    变量及构造方法

    char[] value;
    int count;
    AbstractStringBuilder() {
    }
    AbstractStringBuilder(int capacity) {
        value = new char[capacity];
    }
    

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

    扩容

    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值进行拷贝,这显然是耗时操作

    append()方法

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

    StringBuilder

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

    构造器

    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对象赋初始值。

    append()方法

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

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

    toString()

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

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

    StringBuffer

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

    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被修改了这个变量会被赋值为nullStringBuffertoString如下:

    public synchronized String toString() {
        if (toStringCache == null) {
            toStringCache = Arrays.copyOfRange(value, 0, count);
        }
        return new String(toStringCache, true);
    }
    

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

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

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

    总结

    • StringBuilderStringBuffer都是可变字符串,前者线程不安全,后者线程安全。
    • StringBuilderStringBuffer的大部分方法均调用父类AbstractStringBuilder的实现。其扩容机制首先是把容量变为原来容量的2倍加2。最大容量是Integer.MAX_VALUE,也就是0x7fffffff
    • StringBuilderStringBuffer的默认容量都是16,最好预先估计好字符串的大小避免扩容带来的时间消耗。
  • 相关阅读:
    linux执行命令并获取结果(system)
    awk----基本用法
    shell用法总结
    autoit(au3)使用说明
    博客搜集
    vi常用快捷键总结
    python简单学------------程序传参数,列表推导式,set、list、tuple 转换
    python简单学------------模块
    python简单学------------python面向对象(3)
    python简单学------------python面向对象(2)
  • 原文地址:https://www.cnblogs.com/Tu9oh0st/p/10780641.html
Copyright © 2011-2022 走看看