Java:String、StringBuffer和StringBuilder的异同
Java中与字符串相关的类主要有String、StringBuffer和StringBuilder,他们主要有什么区别,又为什么会这样呢?结合源码来分析一下。
概述
- String:不可变的字符序列,底层使用char[]存储
- StringBuffer:可变的字符序列,线程安全,效率低,底层使用char[]存储
- StringBuilder:可变的字符序列,线程不安全,效率高,底层使用char[]存储
Why?
String
public final class String
implements java.io.Serializable, Comparable<String>, CharSequence {
private final char value[];
}
String类被final修饰,不可继承,String对象的字符内容存储在被fianl修饰的字符数组char[]value中,内容不可修改。对String类型进行修改时,都会生成一个新的 String 对象。以replace()方法为例。
public String replace(char oldChar, char newChar) {
if (oldChar != newChar) {
int len = value.length;
int i = -1;
char[] val = value;
while (++i < len) {
if (val[i] == oldChar) { //查找第一个要被替换的字符位置
break;
}
}
if (i < len) {
char buf[] = new char[len]; //创建长度相同的字符数组
for (int j = 0; j < i; j++) {
buf[j] = val[j]; //复制第一个要被替换的字符之前的内容
}
while (i < len) {
char c = val[i];
buf[i] = (c == oldChar) ? newChar : c;//替换旧字符复制不需要替换的字符
i++;
}
return new String(buf, true); //返回新对象
}
}
return this;
}
StringBuffer和StringBuilder
StringBuffer和StringBuilder都继承自抽象类 AbstractStringBuilder。
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;
AbstractStringBuilder() {
}
AbstractStringBuilder(int capacity) {
value = new char[capacity];
}
}
StringBuffer和StringBuilder在初始化时,当不指定容量(capacity)时默认构造一个容量为16的char型数组。当传入一个int型整数时,构造相应长度的数组,当传入一个String字符串时,构造(字符串长度+16)大小的数组。
String str=""; //char[]value=new char[0];
String str1="abc"; //char[]value=new char[]{'a','b','c'};
StringBuffer sb=new StringBuffer(); //char[]value=new char[16];
StringBuffer sb=new StringBuffer(10); //char[]value=new char[10];
StringBuffer sb1=new StringBuffer(str); //char[]value=new char[str.length()+16];
public StringBuffer() {
super(16);
}
public StringBuffer(int capacity) {
super(capacity);
}
public StringBuffer(String str) {
super(str.length() + 16);
append(str);
}
StringBuffer和StringBuilder都是在原对象的基础上进行修改,但StringBuffer被synchronized修饰线程安全。
public synchronized StringBuffer append(String str) {
toStringCache = null;
super.append(str);
return this; //返回原对象
}
public StringBuilder append(String str) {
super.append(str);
return this; //返回原对象
}
对于String对象来说它的数组长度是固定的,但StringBuffer和StringBuilder的数组长度会随着插入内容而变化。
StringBuffer和StringBuilder在底层实现上大致相同,以StringBuffer为例来查看扩容过程。
public synchronized StringBuffer append(String str) {
toStringCache = null;
super.append(str); //调用父类方法
return this;
}
public AbstractStringBuilder append(String str) {
if (str == null)
return appendNull();
int len = str.length(); //插入字符串的长度
ensureCapacityInternal(count + len); //扩容
str.getChars(0, len, value, count); //将str复制到扩容后的数组中
count += len; //StringBuffer实际长度,即数组中已经插入元素个数
return this;
}
private void ensureCapacityInternal(int minimumCapacity) {
if (minimumCapacity - value.length > 0) { //已满 扩容
value = Arrays.copyOf(value,
newCapacity(minimumCapacity));
//newCapacity(minimumCapacity)确认新数组大小
//public static char[] copyOf(char[] value, int newLength)
//创建大小为newLength的数组,复制value数组内容,并返回新数组
}
}
private int newCapacity(int minCapacity) {
int newCapacity = (value.length << 1) + 2; //默认变为原来的数组长度的2倍+2
if (newCapacity - minCapacity < 0) {
newCapacity = minCapacity;
}
return (newCapacity <= 0 || MAX_ARRAY_SIZE - newCapacity < 0)
? hugeCapacity(minCapacity)
: newCapacity;
}
由以上源码可以看出,当容量不足时进行扩容,一般建立长度为原数组2倍+2的新数组,将原有数组内容复制,返回新数组。扩容会影响插入的效率,在初始化时,应该尽可能的指定容量,避免扩容。
效率
由高到低排列 :StringBuilder> StringBuffer>String
public static void main(String[] args) {
long startTime = 0L;
long endTime = 0L;
String text = "";
StringBuffer buffer = new StringBuffer("");
StringBuilder builder = new StringBuilder("");
//开始对比
startTime = System.currentTimeMillis();
for (int i = 0; i < 20000; i++) {
buffer.append(String.valueOf(i));
}
endTime = System.currentTimeMillis();
System.out.println("StringBuffer的执行时间:" + (endTime - startTime));
startTime = System.currentTimeMillis();
for (int i = 0; i < 20000; i++) {
builder.append(String.valueOf(i));
}
endTime = System.currentTimeMillis();
System.out.println("StringBuilder的执行时间:" + (endTime - startTime));
startTime = System.currentTimeMillis();
for (int i = 0; i < 20000; i++) {
text = text + i;
}
endTime = System.currentTimeMillis();
System.out.println("String的执行时间:" + (endTime - startTime));
}

总结
-
处理字符串,底层都由char[]数组存储内容。.
-
String是不可变字符序列,char型数组被final修饰,不可以修改;
对String对象的修改实际上是创建一个新对象 。 -
StringBuilder和StringBuffer是可变字符序列,都继承自AbstractStringBuilder类,修改是在原对象上进行;
默认创建一个长度为16的char[]数组存储内容,当容量不足时,一般扩容为原数组长度的2倍+2;
StringBuffer被synchronized修饰,线程安全,效率更低,StringBuilder线程不安全,效率更高;
当频繁的修改字符串时,在单线程情况下使用StringBuilder,多线程使用StringBuffer。
本文深入解析Java中String、StringBuffer和StringBuilder的区别与联系,探讨它们的底层实现原理,包括不可变性和可变性、线程安全性及效率对比,适合Java开发者深入理解字符串处理。

229

被折叠的 条评论
为什么被折叠?



