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

全面解释java中StringBuilder、StringBuffer、String类之间的关系

2015-09-30 10:32 218 查看
String的值是不可变的,这就导致每次对String的操作都会生成新的String对象,不仅效率低下,而且大量浪费有限的内存空间,StringBuffer是可变类,和线程安全的字符串操作类,任何对它指向的字符串的操作都不会产生新的对象,StringBuffer和StringBuilder类功能基本相似
1. String 类

  String的值是不可变的,这就导致每次对String的操作都会生成新的String对象,不仅效率低下,而且大量浪费有限的内存空间。

String a = "a"; //假设a指向地址0x0001

a = "b";//重新赋值后a指向地址0x0002,但0x0001地址中保存的"a"依旧存在,但已经不再是a所指向的,a 已经指向了其它地址。

因此String的操作都是改变赋值地址而不是改变值操作。

2. StringBuffer是可变类,和线程安全的字符串操作类,任何对它指向的字符串的操作都不会产生新的对象。 每个StringBuffer对象都有一定的缓冲区容量,当字符串大小没有超过容量时,不会分配新的容量,当字符串大小超过容量时,会自动增加容量。

StringBuffer buf=new StringBuffer(); //分配长16字节的字符缓冲区

StringBuffer buf=new StringBuffer(512); //分配长512字节的字符缓冲区

StringBuffer buf=new StringBuffer("this is a test")//在缓冲区中存放了字符串,并在后面预留了16字节的空缓冲区。

3.StringBuffer

  StringBuffer和StringBuilder类功能基本相似,主要区别在于StringBuffer类的方法是多线程、安全的,而StringBuilder不是线程安全的,相比而言,StringBuilder类会略微快一点。对于经常要改变值的字符串应该使用StringBuffer和StringBuilder类。

4.线程安全

StringBuffer 线程安全

StringBuilder 线程不安全

5.速度

一般情况下,速度从快到慢:StringBuilder<StringBuffer<String,这种比较是相对的,不是绝对的。

6 其实StringBuffer和StringBuilder都是继承的
AbstractStringBuilder
implements java.io.Serializable, CharSequence
实现的原理是利用数组来存储字符,
/**
* The value is used for character storage.
*/
char[] value;
/**
* The count is the number of characters used.
*/
int count;
每次插入之前先保证内存足够
public void ensureCapacity(int minimumCapacity) {
if (minimumCapacity > 0)
ensureCapacityInternal(minimumCapacity);
}
/**
* This method has the same contract as ensureCapacity, but is
* never synchronized.
*/
private void ensureCapacityInternal(int minimumCapacity) {
// overflow-conscious code
if (minimumCapacity - value.length > 0)
expandCapacity(minimumCapacity);
}
/**
* This implements the expansion semantics of ensureCapacity with no
* size check or synchronization.
*/
void expandCapacity(int minimumCapacity) {
int newCapacity = value.length * 2 + 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);
}


疑问:

为什么要(旧值+1)*2呢?

我自己的想法:

也许是考虑到value.length的值可能为0(初始化时设置StringBuffer的capactity为0).

或者考虑到溢出的情况?

但是可能是JVM的某些限制,我的机器数组最大可以设置为30931306,再大就报错:

Exception in thread "main" java.lang.OutOfMemoryError: Java heap space

30931306这个数字好奇怪

还有哪方面的考虑么?谁知道,能告诉我么?
参考http://blog.sina.com.cn/s/blog_7de5c6210100t641.html
http://www.jb51.net/article/33398.htm

6.总结
(1).如果要操作少量的数据用 = String

(2).单线程操作字符串缓冲区 下操作大量数据 = StringBuilder

(3).多线程操作字符串缓冲区 下操作大量数据 = StringBuffer

所以在单线程环境下StringBuilder的性能要好于StringBuffer, 使用字符串常量的相加,而不要使用字符串对象String相加,String对象相加是比较耗时的操作。

以下是代码与演示说明:



复制代码 代码如下:

public class TestString {
private final static int COUNT = 50000; // 循环次数

public TestString() {

}

public void test(String s) {
long begin = System.currentTimeMillis();
for (int i = 0; i < COUNT; ++i)
s += "add";

long over = System.currentTimeMillis();
System.out.println("操作" + s.getClass().getName() + "类型使用的时间为:" + (over - begin) + "毫秒");

}

public void test(StringBuffer s) {
long begin = System.currentTimeMillis();
for (int i = 0; i < COUNT; ++i)
s.append("add");

long over = System.currentTimeMillis();
System.out.println("操作" + s.getClass().getCanonicalName() + "类型使用的时间为:" + (over - begin) + "毫秒");

}

public void test(StringBuilder s) {
long begin = System.currentTimeMillis();
for (int i = 0; i < COUNT; ++i)
s.append("add");

long over = System.currentTimeMillis();
System.out.println("操作" + s.getClass().getName() + "类型使用的时间为:" + (over - begin) + "毫秒");

}

/* 对 String 直接进行字符串拼接的测试 */
public void test2() {
long begin = System.currentTimeMillis();
String s2 = "abcd";
String s = null;
for (int i = 0; i < COUNT; ++i) {
s = s2 + s2 + s2;
}

long over = System.currentTimeMillis();
System.out.println("操作字符串对象引用相加类型使用的时间为:" + (over - begin) + "毫秒");

}

public void test3() {
long begin = System.currentTimeMillis();
String s = null;
for (int i = 0; i < COUNT; ++i) {
s = "abcd" + "abcd" + "abcd";

}
long over = System.currentTimeMillis();
System.out.println("操作字符串相加使用的时间为:" + (over - begin) + "毫秒");

}

public static void main(String[] args) {
String s1 = "abcd";
StringBuffer st1 = new StringBuffer("abcd");
StringBuilder st2 = new StringBuilder("abcd");
TestString tc = new TestString();
tc.test(s1);
tc.test(st1);
tc.test(st2);
tc.test2();
tc.test3();
}
}


运行结果:
操作java.lang.String类型使用的时间为:4105毫秒
操作java.lang.StringBuffer类型使用的时间为:2毫秒
操作java.lang.StringBuilder类型使用的时间为:1毫秒
操作字符串对象引用相加类型使用的时间为:8毫秒
操作字符串相加使用的时间为:0毫秒



内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: