在学习String类中会触及到StringBulider和StringBuffer,那么这三个类有什么区别呢?
为什么说String是最慢的呢?
String为字符串常量,而StringBuilder和StringBuffer均为字符串变量,即String对象一旦创建之后该对象是不可更改的,但后两者的对象是变量,是可以更改的。
举个例子来说String慢的原因
如果运行这段代码会发现先输出“abc”,然后又输出“abcde”,好像是a这个对象被更改了,其实,这只是一种假象罢了,JVM对于这几行代码是这样处理的,首先创建一个String对象str,并把“abc”赋值给a,然后在第三行中,其实JVM又创建了一个新的对象也名为a,然后再把原来的a的值和“de”加起来再赋值给新的a,而原来的a就会被JVM的垃圾回收机制(GC)给回收掉了,所以,a实际上并没有被更改,也就是前面说的String对象一旦创建之后就不可更改了。所以,Java中对String对象进行的操作实际上是一个不断创建新的对象并且将旧的对象回收的一个过程,所以执行速度很慢。
用一句话来总结就是:
而StringBuilder和StringBuffer的对象是变量,对变量进行操作就是直接对该对象进行更改,而不进行创建和回收的操作,所以速度要比String快很多。
当然,有时候我们会这样给String赋值:
这个时候
String a = "abcde"
String a = "abc" + "de"
是完全一样的,所以这个时候String的速度却比StringBuilder的反应速度要快很多。
但是如果写成:
String a = "abc";
a = a + "de";
就会像之前说的一样执行创建删除回收等操作,速度就会慢。
在StringBuilder的源码中
StringBuilder的定义了char的数组,这个数组长度是可变的,它的长度就是count
char[] value;
int count;
先new个StringBuilder对象
StringBuilder sb = new StringBuilder();
StringBuilder的构造方法,默认就是创建16的字符数组,也就是16个字节的大小
public StringBuilder() {
super(16);
}
public StringBuilder(CharSequence seq) {
this(seq.length() + 16);
append(seq);
}
StringBuilder的append的方法
private StringBuilder append(StringBuilder sb) {
if (sb == null)
return append("null");
int len = sb.length();
int newcount = count + len;
if (newcount > value.length)
expandCapacity(newcount);
sb.getChars(0, len, value, count);
count = newcount;
return this;
}
当append字符串的时候,首先会判断sb是否为空,如果不为空就获取sb的长度和StringBuilder已经拼接的字符串长度之和,也就是newcount,如果newcount大于了字符串数组(value)的长度,那么就使用expandCapacity扩充容量。最后使用sb.getChars()将新添加的字符串添加到字符串数组(value)中,并将newcount的值赋予count。
private void ensureCapacityInternal(int minimumCapacity) {
if (minimumCapacity - value.length > 0)
expandCapacity(minimumCapacity);
}
ensureCapacityInternal方法又调用了expandCapacity方法
oid 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);
}
先扩充16*2+2字节的容量(34字节),然后判断新扩充的字节是否大于StringBuilder的拼接字符串的长度(新添加的+已经有的字符串的长度),如果小于的话,那么新扩充的长度newCapacity就赋予StringBuilder的拼接的字符串的长度。如果newCapacity<0说明oom异常了(内存不够用了),那么这时候newCapacity = Integer.MAX_VALUE。最后在把字符串数组value的内存扩充。
总结: