2019-08-20

浅谈String、StringBuffer 、StringBuilder和StringJoiner

可变性

String 类中使用 final 关键字修饰字符数组来保存字符串,private final char value[],所以 String 对象是不可变的。而StringBuilder 与 StringBuffer 都继承自 AbstractStringBuilder 类,在 AbstractStringBuilder 中使用字符数组保存字符串char[] value, 没有用 final 关键字修饰,所以这两种对象都是可变的。

安全性

String 中的对象是不可变的,线程安全。
StringBuffer 对方法加了同步锁或者对调用的方法加 了同步锁,所以是线程安全的。
下面是StringBuffer 源码中的部分代码:

    @Override
    public synchronized int length() {
        return count;
    }

    @Override
    public synchronized int capacity() {
        return value.length;
    }

    @Override
    public synchronized void ensureCapacity(int minimumCapacity) {
        super.ensureCapacity(minimumCapacity);
    }

StringBuilder 可变,但是没有对方法加同步锁,所以是非线程安全的。

性能

每次对 String 类型进行改变的时候,都会生成一个新的 String 对象,然后将指针指向新的 String 对象。StringBuffer 与StringBuilder 每次都是对对象本身进行操作,而不是生成新的对象并改变对象引用。相同情况下使用 StringBuilder 相比使用 StringBuffer 能获得 10%~15% 左右的性能提升,但却有多线程不安全的风险。

对于三者使用的总结

操作少量的数据: 适用String
单线程操作字符串缓冲区下操作大量数据: 适用StringBuilder
多线程操作字符串缓冲区下操作大量数据: 适用StringBuffer

AbstractStringBuilder 扩容机制

由于StringBuilder 与 StringBuffer 都继承自 AbstractStringBuilder 类,所以就来简单的探讨一下StringBuilder的扩容机制,StringBuffer 只需在此基础上加上同步锁就是。
下面我们在源码中分析:

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,可以传入参数(CharSequence为可读可写序列,意思还可以传一个StringBuilder等的变量)。

   /*
     AbstractStringBuilder 中成员变量
   */
    char[] value;//StringBuilder本质就是可变长度的字符数组
    int count;//已有内容的长度
/*
这个方法保证minimumCapacity > 0
*/
 public void ensureCapacity(int minimumCapacity) {
        if (minimumCapacity > 0)
            ensureCapacityInternal(minimumCapacity);
    }

/*
这个方法就是获得一个长度较大的字符数组
*/
  private void ensureCapacityInternal(int minimumCapacity) {
        if (minimumCapacity - value.length > 0) {
            value = Arrays.copyOf(value,
                    newCapacity(minimumCapacity));
        }
    }

/*
这个方法就是获取value新的值max(value.length*2+2,minimumCapacity)
*/
 private int newCapacity(int minCapacity) {
        int newCapacity = (value.length << 1) + 2;//左移一位,扩大两倍
        if (newCapacity - minCapacity < 0) {
            newCapacity = minCapacity;
        }
        return (newCapacity <= 0 || MAX_ARRAY_SIZE - newCapacity < 0)
            ? hugeCapacity(minCapacity)
            : newCapacity;
    }


/*
获取容量的最大值,防止内存溢出
*/
private int hugeCapacity(int minCapacity) {
        if (Integer.MAX_VALUE - minCapacity < 0) { // 内存溢出
            throw new OutOfMemoryError();
        }
        return (minCapacity > MAX_ARRAY_SIZE)
            ? minCapacity : MAX_ARRAY_SIZE;
    }

对于大数据的2进制运算,位移运算符比那些普通运算符的运算要快很多,因为程序仅仅移动一下而已,不去计算,这样提高了效率,节省了资源

这里简单说说StringJoiner

StringJoiner(JDK1.8),在源码中可以看到,内部其实运用的是StringBuilder ,感觉就是在StringBuilder 的基础上可以快速拼接字符串。

public final class StringJoiner {
    private final String prefix;//开头使用的字符序列 
    private final String delimiter;//要添加到每个元素之间的间隔符
    private final String suffix;// 最后使用的字符序列
    private StringBuilder value;//内部运用StringBuilder 
}
/*
添加内容是调用StringBuilder的append()添加方法
*/
public StringJoiner add(CharSequence newElement) {
        prepareBuilder().append(newElement);
        return this;
    }
/*
返回一个StringBuilder的值
*/
 private StringBuilder prepareBuilder() {
        if (value != null) {
            value.append(delimiter);
        } else {
            value = new StringBuilder().append(prefix);//若为null就new StringBuilder()
        }
        return value;
    }

你可能感兴趣的:(2019-08-20)