浅谈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;
}