java清空StringBuffer的方法分析

方法汇总

方法1:指向新的内存空间。即通过赋予新的实例的方式,使引用执行新的空的内存空间。

StringBuffer stringBuffer = new StringBuffer();
stringBuffer = new StringBuffer(); //通过赋值让引用执行新的内存空间

方法2:删除全部字符。即通过delete方法,从开始位删除到结尾位。

StringBuffer stringBuffer = new StringBuffer();
stringBuffer.append("Hello world!");
int length = stringBuffer.length();
stringBuffer.delete(0, length);  // 删除stringBuffer保存的全部字符

方法3:设置长度为0。StringBuffer内部是通过字符数组的形式来保存内容的。StringBuffer有两个从父类AbstractStringBuilder中继承的成员属性,分别是用于存储内存的字符数组char[] value,和计算数组value中使用量的int count。设置长度为0就是通过setLength方法将count置零,让新传入的内容从value的0位置开始存储。

StringBuffer stringBuffer = new StringBuffer();
stringBuffer.append("Hello world!");
stringBuffer.setLength(0);   //设置stringBuffer的长度为0

方法对比

对比角度1:源码分析

1、指向新的内存空间

    这种方式需要创建新的内存。从开销角度,是new一个实例的开销;但是从对JVM的影响的角度,丢弃原本的实例去执行新创建的实例的做法,会加速gc的到来,并不推荐。

2、删除全部字符

    查看源码:

@Override
public synchronized StringBuffer delete(int start, int end) {
    toStringCache = null;
    super.delete(start, end);
    return this;
}

    可以看到,StringBuffer的delete方法是通过调用父类的delete方法来实现的。查看父类AbstractStringBuilder的delete方法:

public AbstractStringBuilder delete(int start, int end) {
    if (start < 0)
        throw new StringIndexOutOfBoundsException(start);
    if (end > count)
        end = count;
    if (start > end)
        throw new StringIndexOutOfBoundsException();
    int len = end - start;
    if (len > 0) {
        System.arraycopy(value, start+len, value, start, count-end);
        count -= len;
    }
    return this;
}

    通过源码可以看出,传入合法的start和end参数的情况下,需要执行四次判断操作、一次减法和赋值操作,然后去调用System的arraycopy方法。查看System的arraycopy方法可以发现,这是一个本地方法。本地方法的调用,涉及到初次调用时本地方法的加载、java参数于本地方法参数的转换等的开销,以及由于JVM的GC无法管理本地方法内存,本地方法的使用者需要自行关注内存的创建、使用和销毁等问题,同时本地方法也无法享受JVM优化的成功。所以在涉及到本地方法调用的时候应该谨慎。System的arraycopy方法:

public static native void arraycopy(Object src,  int  srcPos, Object dest, int destPos, int length);

3、设置长度为0

    查看setLength方法的源码:

@Override
public synchronized void setLength(int newLength) {
    toStringCache = null;
    super.setLength(newLength);
}

    setLength方法也是通过调用父类的方法来实现的。继续去查看父类的代码:

public void setLength(int newLength) {
        if (newLength < 0)
            throw new StringIndexOutOfBoundsException(newLength);
        ensureCapacityInternal(newLength);

        if (count < newLength) {
            Arrays.fill(value, count, newLength, '\0');
        }

        count = newLength;
    }

    根据我们的需要,输入参数应该设置为0,根据代码可知,会经历两次判断和一次赋值,操作明显比删除全部字符要简单。

总结

    从源码的角度,首选应该是使用setLength方法设置长度为0。

    那么不妨用代码测试一下,是不是这种方法最快。

对比角度2:代码测试

    测试代码:

public class StringBufferClearer {
  public static void main(String[] args) {
    StringBuffer content = new StringBuffer();
    for (int i = 0; i < 10; i++) {
      content.append("Hello, world!");
    }
    String data = content.toString();

    int count = 10000 * 10000;

    // test way 1:
    long timeBegin1 = System.currentTimeMillis();
    for (int i = 0; i < count; i++) {
      StringBuffer stringBuffer = new StringBuffer();
      stringBuffer.append(data);
      stringBuffer = new StringBuffer();
    }
    System.out.println("new StringBuffer() time cost: " + (System.currentTimeMillis() - timeBegin1));

    // test way 2:
    long timeBegin2 = System.currentTimeMillis();
    for (int i = 0; i < count; i++) {
      StringBuffer stringBuffer = new StringBuffer();
      stringBuffer.append(data);
      stringBuffer.delete(0, stringBuffer.length());
    }
    System.out.println("delete time cost: " + (System.currentTimeMillis() - timeBegin2));

    // test way 3:
    long timeBegin3 = System.currentTimeMillis();
    for (int i = 0; i < count; i++) {
      StringBuffer stringBuffer = new StringBuffer();
      stringBuffer.append(data);
      stringBuffer.setLength(0);
    }
    System.out.println("setLength time cost: " + (System.currentTimeMillis() - timeBegin3));
  }
}

    测试结果:

new StringBuffer() time cost: 6482
delete time cost: 5238
setLength time cost: 5096

总结

    根据测试结果,性能确实和预料基本相似。所以,推荐优先使用setLength方法设置长度为0。当然,delete方法开销的差距也并不大。

你可能感兴趣的:(Java基础)