深入StringBuffer
作者:eckel_cn
最近,闲暇之余,重新整理自用的StringUtil类,在整理到repeat方法(重复字符串)时,发现其存在效率问题,便打算重新写一下,却发现了一系列以前没太注意的东西,觉得很有必要写下来.
初一看,就能发现一点问题,当n很大,达到一定大小,就能使堆栈溢出.出现OutOfMemery错误.
那如何避免这个问题呢,我马上想到了,既然StringBuffer和String一样内部都是在操作char数组,
那我就自己直接操作char数组,在最少的循环里面,填充char数组,代码如下:
1
public static String repeat2(String str, int n)
{
2
int len = str.length();
3
int maxlen = len * n;
4
char[] chars = new char[len * n];
5
str.getChars(0, len, chars, 0);
6
int count = len;
7
int copyLen = 0;
8
while ((copyLen = maxlen - count > count ? count : maxlen - count) > 0)
{
9
System.arraycopy(chars, 0, chars, count, copyLen);
10
count += copyLen;
11
}
12
String outstr = new String(chars);
13
return outstr;
14
}
测试后,发现,循环控制在了最少,但是,性能却还不如repeat1,也许你会和我一样开始很惊讶,但看了
StringBuffer的实现后,发现repeat2的实现中,将char数组,转换为String,是效率多么低的事情,这个过程需要在String内部重新创建一个char数组,然后把传进去的char数组,复制给它.而repeat1中为什么没有这个性能问题呢,其实这就是要归功于SUN在实现StringBuffer中的一个重要属性:shared,当你使用StringBuffer后,想得到String时,String会共享StringBuffer中的char数组,这样一来,性能非常高.
共享会不会带来副作用呢,SUN的实现当然考虑到了,在改变StringBuffer时,如果这个StringBuffer
有其他String共享它的char数组时,StringBuffer就把这个char数组让给String,自己重新复制一份使用.
既然问题发现了,我又换了种算法来实现,同样也是把循环控制在最少,同时解决转换带来的效率低下问题.
1
public static String repeat3(String str, int n)
{
2
// if input string is null,return null.
3
if (null == str)
{
4
return null;
5
}
6
final int strlen = str.length();
7
// if repeat number is less than two or given string's length is zero,
8
// then return the givien string.
9
if (2 > n || 0 == strlen)
{
10
return str;
11
}
12
// assigns the enough size for the string buffer.
13
StringBuffer strBuf = new StringBuffer(strlen * n);
14
// get odd length flag.
15
final boolean oddLen = n % 2 == 1 ? true : false;
16
// calculates the grow times.
17
final int growTimes = (int) Math.floor(Math.log(n) / Math.log(2));
18
// adds one given string.
19
strBuf.append(str);
20
// grows until more than half of the repeat count.
21
for (int i = 0; i < growTimes; i++)
{
22
strBuf.append(strBuf);
23
if (Math.pow(2, i - 1) * 2 < n && Math.pow(2, i) * 2 >= n)
{
24
break;
25
}
26
}
27
// reset the string buffer's length to half of the repeat count.
28
strBuf.setLength(strlen * Math.round(n / 2));
29
// grows to the repeat count.
30
strBuf.append(strBuf);
31
if (oddLen)
{
32
strBuf.append(str);
33
}
34
String returnStr = new String(strBuf);
35
return returnStr;
36
}
37
算法实现,再次测试,测试发现,在规模不是特别大的情况下,repeat3的效率要明显优于前两个,但在规模大的情况下,会变的差不多,怎么会这样的呢,再次进行深入调查,结果发现是SUN的System.arrayCopy这个方法有性能问题,特别是当要复制的长度特别大的情况下,性能下降的比较快,再换个IBM的JDK实现,又发现,IBM的这个方法的实现要比SUN有改进.
真是一次比较有意思的深入StringBuffer,在实际运用中repeat3完全可以代替上面两种方法.