关于String.concat, StringBuilder, StringBuffer和+

一. 性能方面

StringBuilder > StringBuffer > concat > +
测试代码:

private static StringBuilder stringBuilder = new StringBuilder();

  private static StringBuffer stringBuffer = new StringBuffer();

  private static String string = "";

  private static String string2 = "";
  @Test
  public void testAdd() {
    long start = System.currentTimeMillis();
    String str = "";
    for (int i = 0; i < 400000; i++) {
      str = str + "a";
    }
    System.out.println("1:"+ (System.currentTimeMillis() - start));

    long start2 = System.currentTimeMillis();
    StringBuilder sb = new StringBuilder();
    for (int i = 0; i < 400000; i++) {
      sb.append("a");
    }
    System.out.println("2:"+(System.currentTimeMillis() - start2));

    long start3 = System.currentTimeMillis();
    StringBuffer sb2 = new StringBuffer();
    for (int i = 0; i < 400000; i++) {
      sb2.append("a");
    }
    System.out.println("3:"+(System.currentTimeMillis() - start3));

    long start4 = System.currentTimeMillis();
    String str4 = "";
    for (int i = 0; i < 400000; i++) {
      str4 = str4.concat("a");
    }
    System.out.println("4:"+(System.currentTimeMillis() - start4));
  }
结果:
1:25787
2:6
3:6
4:17546
实现原理:

首先StringBuilder和StringBuffer的区别在于StringBuffer的所有public方法都加上了synchronized关键字,保证线程安全,而从根本实现层面来说,四者都是通过调用System.arraycopy()实现的:

1. StringBuilder:

public AbstractStringBuilder append(String str) {
        if (str == null)
            return appendNull();
        int len = str.length();
        ensureCapacityInternal(count + len);
        str.getChars(0, len, value, count);
        count += len;
        return this;
    }

private void ensureCapacityInternal(int minimumCapacity) {
        // overflow-conscious code
        if (minimumCapacity - value.length > 0) {
            value = Arrays.copyOf(value,
                    newCapacity(minimumCapacity));
        }
    }

public void getChars(int srcBegin, int srcEnd, char dst[], int dstBegin) {
        if (srcBegin < 0) {
            throw new StringIndexOutOfBoundsException(srcBegin);
        }
        if (srcEnd > value.length) {
            throw new StringIndexOutOfBoundsException(srcEnd);
        }
        if (srcBegin > srcEnd) {
            throw new StringIndexOutOfBoundsException(srcEnd - srcBegin);
        }
        System.arraycopy(value, srcBegin, dst, dstBegin, srcEnd - srcBegin);
    }

可以看到,通常情况下,一次append()会调用两次System.arraycopy()方法

2. oncat:

public String concat(String var1) {
    int var2 = var1.length();
    if (var2 == 0) {
      return this;
    } else {
      int var3 = this.value.length;
      char[] var4 = Arrays.copyOf(this.value, var3 + var2);
      var1.getChars(var4, var3);
      return new String(var4, true);
    }
  }

public static char[] copyOf(char[] original, int newLength) {
        char[] copy = new char[newLength];
        System.arraycopy(original, 0, copy, 0,
                         Math.min(original.length, newLength));
        return copy;
    }

void getChars(char[] var1, int var2) {
    System.arraycopy(this.value, 0, var1, var2, this.value.length);
  }

可以看到,一次concat()也是调用了两次System.arraycopy()方法,但是每次调用concat()方法都会调用一次new String()创建一个新的string对象,因此concat操作耗时较长

3. +

+运算符被重载,但是底层也是通过调用StringBuilder.append()实现的(jdk1.6 以上),而它之所以耗时是因为每次调用都会创建一个新的StringBuilder对象。

二 线程安全

测试代码和结果

@Test
  public void threadSafeTest(){
    ExecutorService executor = Executors.newCachedThreadPool();
    for(int i = 0 ;i < 5000 ;i++){
      executor.execute( () -> {
        stringBuilder.append("a");
          }
      );
    }
    System.out.println("stringBuilder:"+stringBuilder.length());

    long start2 = System.currentTimeMillis();
    for(int i = 0 ;i < 5000 ;i++){
      executor.execute( () -> {
            stringBuffer.append("a");
          }
      );
    }
    System.out.println("stringBuffer:"+stringBuffer.length());

    for(int i = 0 ;i < 5000 ;i++){
      executor.execute( () -> {
            string = string.concat("a");
          }
      );
    }
    System.out.println("concat:"+string.length());

    for(int i = 0 ;i < 5000 ;i++){
      executor.execute( () -> {
            string2 = string2 + "a";
          }
      );
    }
    System.out.println("+:"+string2.length());
  }

stringBuilder:4987
stringBuffer:5000
concat:4139
+:3260

可以看到,只有StringBuffer是线程安全的,其他几种拼接方式都不是线程安全的(如果length = 5000,那么代表是线程安全的)

你可能感兴趣的:(关于String.concat, StringBuilder, StringBuffer和+)