JAVA---揭秘StringBuffer的capacity

原文地址:http://blog.sina.com.cn/s/blog_7de5c6210100t641.html
前几天写了一篇文章“ 如何让你的程序运行的更快(1)—String VS StringBuffer ”,文章在情景三中提到了如何通过“设置StringBuffer的容量来提升性能”,其中有个问题我没有想明白,就是为什么StringBuffer的容量自动增加的时候是“2*旧值+2”呢?
虽然问题依然没有解决,不过也发现了不少有趣的问题,在此和大家分享 。希望能让你有所收获,欢迎大家一起讨论。
注:需要用到的函数说明:
capacity():Returns the current capacity of the String buffer.
The capacity is the amount of storage available for newly inserted characters;
beyond which an allocation will occur.
length(): Returns the length (character count) of this string buffer.

一.StringBuffer的默认capacity
例1:
StringBuffer sb = new StringBuffer();
System.out.println( ” with no characters, the initial capacity of StringBuffer is ” + sb.capacity());
System.out.println( ” and the length of the StringBuffer is ” + sb.length());
输出:
with no characters, the initial capacity of StringBuffer is 16
and the length of the StringBuffer is 0
结论: StringBuffer的默认容量(capacity)为16
原因:
StringBuffer的默认构造函数为
public StringBuffer() {
this ( 16 );
}
此时默认构造函数又调用了StringBuffer的代参数的构造函数,设置字符串数组value长度为16,如下:
private char value[]; //The value is used for character storage.
private boolean shared; // A flag indicating whether the buffer is shared

public StringBuffer( int length) {
value = new char [length];
shared = false ;
}

// 调用capacity()返回字符串数组value的长度,即StringBuffer的容量(capacity)
public synchronized int capacity() {
return value.length;
}

二.用字符串初始化StringBuffer的内容

在声明一个StringBuffer变量的时候,用字符串进行初始化,容量会有变化么?
例2:
// 声明并初始化
StringBuffer sb1 = new StringBuffer( ” hello world ” );
System.out.println( ” with characters, the capacity of StringBuffer is ” + sb1.capacity());
System.out.println( ” but the length of the StringBuffer is ” + sb1.length());
StringBuffer的capacity
// 利用append()来设置StringBuffer的内容
StringBuffer sb11 = new StringBuffer();
sb11.append( ” hello world ” );
System.out.println( ” with append(), the capacity of StringBuffer is ” + sb11.capacity());
System.out.println( ” but the length of the StringBuffer is ” + sb11.length());

两者输出结果会一样么?
你一定认为,这不是显然的么。用长度为11的字符串“hello world”进行初始化,其长度11小于StringBuffer的默认容量16。所以两者结果都为capacity=16,length=11。
那么实际结果如何呢?
输出:
with characters, the capacity of StringBuffer is 27
but the length of the StringBuffer is 11
with append(), the capacity of StringBuffer is 16
but the length of the StringBuffer is 11
疑问:
怎么第一种方法的StringBuffer的capacity是27(16+11)呢?

原因:
StringBuffer的带参数的构造函数
1 public StringBuffer(String str) {
2 this(str.length() + 16 );
3 append(str);
4 }
结论:
StringBuffer的capacity等于用来初始化的字符串长度(11)加上StringBuffer的默认容量(16),而不是我们想当然的在默认容量16中拿出11个来存放字符串“hello world”。
如果我们不设置StringBuffer的capacity,分别对两者继续追加字符串,任其自动增长,其容量增长如下:
第一种情况:27,56,114,230,462,926…,
第二种情况:16,34,70 ,142,286,574…,
(为什么容量增加会是这种规律,后面会做解释)。

我想情况2节省空间的概率大一些,因为StringBuffer的capacity的增长比情况1慢,每次增加的空间小一些。
所以以后写代码的时候可以考虑使用第二种方法(使用StringBuffer的append()),特别是初始化字符串很长的情况。当然这会多写一行代码^+^:

三.StringBuffer的capacity变化
例3:
StringBuffer sb3 = new StringBuffer();
for ( int i = 0 ; i < 12 ; i ++ ){
sb3.append(i);
}
System.out.println( ” before changed, the capacity is ” + sb3.capacity());
System.out.println( ” and the length is ” + sb3.length());

for ( int i = 0 ; i < 10 ; i ++ ){
sb3.append(i);
}
System.out.println( ” first time increased, the capacity is ” + sb3.capacity());
System.out.println( ” and the length is ” + sb3.length());

for ( int i = 0 ; i < 11 ; i ++ ){
sb3.append(i);
}
System.out.println( ” second time increased, the capacity is ” + sb3.capacity());
System.out.println( ” and the length is ” + sb3.length());
输出:
before changed, the capacity is 16
and the length is 14
first time increased, the capacity is 34
and the length is 24
second time increased, the capacity is 70
and the length is 36
奇怪,benfore changed怎么长度不是12而是14呢?哈哈,开始我也困惑了一下,仔细想想就会明白的,你可以输出sb3看看System.out.println(“the content of sb3 is ” + sb3.toString());
结论:
capacity增长的规律为 (旧值+1)*2
原因:
StringBuffer的容量增加函数expandCapacity():
private void expandCapacity( int minimumCapacity) {
int newCapacity = (value.length + 1 ) * 2 ;
if (newCapacity < 0 ) {
newCapacity = Integer.MAX_VALUE;
} else if (minimumCapacity > newCapacity) {
newCapacity = minimumCapacity;
}

char newValue[] = new char [newCapacity];
System.arraycopy(value, 0 , newValue, 0 , count);
value = newValue;
shared = false ;
}
疑问:
为什么要(旧值+1)*2呢?

我自己的想法:
也许是考虑到value.length的值可能为0(初始化时设置StringBuffer的capactity为0).
或者考虑到溢出的情况?
但是可能是JVM的某些限制,我的机器数组最大可以设置为30931306,再大就报错:
Exception in thread “main” java.lang.OutOfMemoryError: Java heap space

30931306这个数字好奇怪

还有哪方面的考虑么?谁知道,能告诉我么?

四.StringBuffer的capacity只能大不能小

例5:
1 StringBuffer sb4 = new StringBuffer();
2 System.out.println(“before ensureCapacity(), the capacity is ” + sb4.capacity());
3 sb4.ensureCapacity(10 );
4 System.out.println(“after ensureCapacity(10), the capacity is ” + sb4.capacity());
5
6 System.out.print(“now, the capacity is ” + sb4.capacity() + “, ” );
7 sb4.ensureCapacity(20 );
8 System.out.println(“after ensureCapacity(20), the capacity is ” + sb4.capacity());
9
10 System.out.print(“now, the capacity is ” + sb4.capacity() + “, ” );
11 sb4.ensureCapacity(80 );
12 System.out.println(“after ensureCapacity(80), the capacity is ” + sb4.capacity());
输出:
before ensureCapacity(), the capacity is 16
after ensureCapacity( 10 ), the capacity is 16
now, the capacity is 16 , after ensureCapacity( 20 ), the capacity is 34
now, the capacity is 34 , after ensureCapacity( 80 ), the capacity is 80
结论:
当设置StringBuffer的容量
1、小于当前容量时,容量不变。
本例中,容量依然为16。
2、大于当前容量,并且小于(当前容量+1)*2,则容量变为(当前容量+1)*2。
本例中,16<20<(16+1)*2=34,所以容量为34。
3、大于当前容量,并且大于(当前容量+1)*2,则容量变为用户所设置的容量。
本例中,80>16,80>(16+1)*2=34,所以容量为80。

原因:
函数:ensureCapacity( )和 expandCapacity( )进行了控制
public synchronized void ensureCapacity( int minimumCapacity) {
if (minimumCapacity > value.length) {
// 当设置StringBuffer的容量小于当前容量时,容量不变。
expandCapacity(minimumCapacity);
}
}

private void expandCapacity( int minimumCapacity) {
int newCapacity = (value.length + 1 ) * 2 ;
if (newCapacity < 0 ) {
newCapacity = Integer.MAX_VALUE;
} else if (minimumCapacity > newCapacity) {
// 当设置StringBuffer的容量大于(当前容量+1)*2,则容量变为用户所设置的容量。
// 否则,容量为(当前容量+1)*2,即newCapacity
newCapacity = minimumCapacity;
}

char newValue[] = new char [newCapacity];
System.arraycopy(value, 0 , newValue, 0 , count);
value = newValue;
shared = false ;
}

注:
问一下
String str = String.valueOf(null);
System.out.println(str.length());
你们执行的话会出错么?
我的报错,我的是jdk1.4
因为StringBuffer中的append(String str)函数中有这样的语句,
public synchronized StringBuffer append(String str) {
if (str == null ) {
str = String.valueOf(str);
}

int len = str.length(); // len有可能得负值么?
int newcount = count + len;
if (newcount > value.length)
expandCapacity(newcount);
str.getChars( 0 , len, value, count);
count = newcount;
return this ;
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值