博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
如何让你的程序运行的更快(1)之续---揭秘StringBuffer的capacity
阅读量:6618 次
发布时间:2019-06-25

本文共 6591 字,大约阅读时间需要 21 分钟。

前几天写了一篇文章“ ”,文章在情景三中提到了如何通过“设置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()); 
None.gif
// 
利用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 
;

 本文转自BlogJavaOo缘来是你oO的博客,原文链接:,如需转载请自行联系原博主。

你可能感兴趣的文章
如何生成项目的chm文档
查看>>
java封装httpClient工具(支持http和https,包含get和post请求)
查看>>
Rocket - diplomacy - LazyModuleImpLike
查看>>
Exchange Server 2016管理系列课件25.管理安全通讯组
查看>>
计算机科学,大一学生怎样来爱你(文&PPT)
查看>>
PHP 开发社区微信服务号实战图解
查看>>
Exchange Server 2013 系列八:邮箱服务器角色DAG实战
查看>>
php使用curl下载指定大小的文件
查看>>
VS2013创建Node.js C++ Addons的过程
查看>>
amaze ui中的icon button
查看>>
tcp 三次握手
查看>>
XML中添加换行符
查看>>
在C#中使用属性控件添加属性窗口
查看>>
printf()详解之终极无惑
查看>>
交叉检验---训练数据,验证数据和测试数据
查看>>
AspNetPager分页控件配置
查看>>
第 8 章 Spring Data
查看>>
[裴礼文数学分析中的典型问题与方法习题参考解答]5.1.24
查看>>
C语言 编程练习22题
查看>>
CloudDBA现场助力双十一
查看>>