您的位置:首页 > 产品设计 > UI/UE

Java String、StringBuffer、StringBuilder深度剖析

2016-04-28 12:17 615 查看

三种字符串构造方式

String方式

String testString = "a";
for (int i = 0; i < 10; i++) {
testString += "b";
}


StringBuffer方式

StringBuffer sbuf = new StringBuffer();
for (int i = 0; i < 10; i++) {
sbuf.append("b");
}


StringBuilder方式

StringBuilder sbud = new StringBuilder();
for (int i = 0; i < 10; i++) {
sbud.append("b");
}


三种方案剖析

利用String的 a+=”b”这种方式来构造字符串在我们平时用的比较多,用起来也很方便,但是总有一些有开发经验的前辈们会说:“尽量少使用这种方式,应该多使用StringBuffer、StringBuilder等方式来构造,这种方式会降低整个程序的性能!”,但是至于为什么使用这种方式会降低程序的性能,我们无从得知。今天我编写这篇博客的目的就是对这三种方式进行深入剖析,让读者真正的了解为什么!

1. String构造方式解析

我们常常会使用
String testString = "a"; testString  += "b";
这种方式来构造字符串,但是却被告知这种方式会降低性能,这到底是怎么一回事呢?接下来我会通过分析源码的方式给大家仔细分析为什么!

String.java

// 为什么说String不是基本数据类型的原因也可以从这段代码中看出来
// String字符串的实现方式是建立在字符数组之上的
public final class String
implements java.io.Serializable, Comparable<String>, CharSequence {
/** The value is used for character storage. */
/** String的内容是存放在字符数组中的. final意味着字符串的内容是只读的,不可修改的*/
private final char value[];
....
}


大家从这段代码中看到了什么?final关键字,这意味着String从一开始初始化,它的内容是不允许修改的,那么我们平时通过
String testString = "a"; testString  = "b";
这种方式对字符串重新赋值是怎么做到的呢,既然我们不能修改String的值,那就只能重新创建一个String对象了,然后让testString指向新的String对象的地址即可,这就意味着每一次执行赋值(testString=“a”或者testString += “a”)都会重新创建一个对象,然而创建对象和销毁对象是一个浪费时间和空间的过程,所以有过经验的开发者都会说,尽量不要使用这种方式构造字符串,这也是为什么说这种方式会影响程序性能的原因了。

2. StringBuffer方式的构造

AbstractStringBuilder.java

abstract class AbstractStringBuilder implements Appendable, CharSequence {
/**
* The value is used for character storage.
*/
/* 存放字符串的字符数组 */
char[] value;
......
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;
}
}


StringBuffer.java

// 继承AbstractStringBuilder,所以说也继承了char[] value;
public final class StringBuffer
extends AbstractStringBuilder
implements java.io.Serializable, CharSequence
{

/**
* A cache of the last value returned by toString. Cleared
* whenever the StringBuffer is modified.
*/
/* 缓存上一次toString方法返回的值,每次append或者其他修改操作的时候都会清空*/
private transient char[] toStringCache;
......
public synchronized int offsetByCodePoints(int index, int codePointOffset) {
return super.offsetByCodePoints(index, codePointOffset);
}

@Override
public synchronized StringBuffer append(Object obj) {
toStringCache = null;
super.append(String.valueOf(obj));
return this;
}

@Override
public synchronized StringBuffer append(String str) {
toStringCache = null;
super.append(str);
return this;
}
......
@Override
public synchronized String toString() {
// 生成String对象
if (toStringCache == null) {
toStringCache = Arrays.copyOfRange(value, 0, count);
}
return new String(toStringCache, true);
}


大家从上面的代码有没有发现什么?synchronized关键字、char[] value已经没有用final修饰了。synchronized修饰意味着这是线程安全的,适合多线程使用;value不再使用final修饰意味着每次insert、append只是纯粹的修改了value的值,而没有重新创建新的String对象,减少了对象创建和销毁的过程,更高效。

总结一下两点

1. synchronized修饰,可以多线程使用,但是锁的争用降低效率,如果单线程想要使用此方法构造,同样会存在上锁和释放锁的过程,效率低下。

2. char[] value已经没有用final修饰了,字符串的构造过程只是值的修改过程,没有对象的创建,效率更高。

3. StringBuilder构造方式的剖析

AbstractStringBuilder.java

// 和StringBuffer一样的父类
abstract class AbstractStringBuilder implements Appendable, CharSequence { /** * The value is used for character storage. */ /* 存放字符串的字符数组 */ char[] value; ...... 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; } }


StringBuilder.java

// 和StringBuffer一样继承AbstractStringBuilder
// 字符串存放在AbstractStringBuilder的value中
public final class StringBuilder
extends AbstractStringBuilder
implements java.io.Serializable, CharSequence
{
......
@Override
public StringBuilder append(Object obj) {
return append(String.valueOf(obj));
}

@Override
public StringBuilder append(String str) {
super.append(str);
return this;
}
......
@Override
public String toString() {
// Create a copy, don't share the array
return new String(value, 0, count);
}
}


大家可以看到方法修饰少了synchronized关键字,少了toStringCache(并没有什么特别的影响),除此之外和StringBuffer是差不多的。少了synchronized更方便单线程使用,提高了效率。

总结

通过上面我们分别对String、StringBuffer、StringBuilder的源码分析,总结三种方式进行字符串构造的优缺点如下:

1. String字符串构造方式会不断的创建和销毁对象,时间和空间浪费大

2. StringBuffer方式不会频繁的创建和销毁对象,性能大大提升,而且操作是多线程安全的,所以适合多线程的字符串构造,但是对于单线程来说仍然存在上锁和释放锁的过程,增加了构造的时间,不利于单线程操作

3. StringBuilder大体上同StringBuffer一样,但是没有加入多线程的安全机制,不适合多线程使用,但是由于没有引入多线程安全的机制,所以去除了上锁和释放锁的过程,减少了构造的时间,适合单线程使用
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: