Eckel's world

The program should be extensible and reusable.
随笔 - 3, 文章 - 0, 评论 - 2, 引用 - 0
数据加载中……

深入StringBuffer

深入StringBuffer
作者:eckel_cn
    最近,闲暇之余,重新整理自用的StringUtil类,在整理到repeat方法(重复字符串)时,发现其存在效率问题,便打算重新写一下,却发现了一系列以前没太注意的东西,觉得很有必要写下来.
原来这个repeat方法,很简单,主要代码大致如下:
1public static String repeat1(String str, int n) {
2StringBuffer strBuf = new StrBuffer(str.length()*n);
3for(int i - 0; i < n; i++{
4    strBuf.append(str);
5}

6retrn strBuf.toString();
7}
    初一看,就能发现一点问题,当n很大,达到一定大小,就能使堆栈溢出.出现OutOfMemery错误.
那如何避免这个问题呢,我马上想到了,既然StringBuffer和String一样内部都是在操作char数组,
那我就自己直接操作char数组,在最少的循环里面,填充char数组,代码如下:
 1public 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,自己重新复制一份使用.
既然问题发现了,我又换了种算法来实现,同样也是把循环控制在最少,同时解决转换带来的效率低下问题.
 1public 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完全可以代替上面两种方法.

posted on 2005-11-18 17:25 eckelcn 阅读(872) 评论(0)  编辑  收藏 所属分类: JAVA(未分类)


只有注册用户登录后才能发表评论。


网站导航: