String、StringBuffer、StringBuilder三兄弟

三兄弟介绍

在开发过程中我们会经常用到String类型,查看源码可知String被声明为final class,即String类型不可变,正因为他的不可变性,当我们拼接字符串时会产生很多无效的中间对象,频繁的使用String字符串做拼接,会产生大量的垃圾对象,影响系统的性能。
但开发过程中我们又不能避免拼接字符串情况的出现,Java为我们提供了StringBuffer,本质为一个线程安全的可修改的字符序列,提供append和add方法来拼接字符串,修改数据的方法都被加上了synchronized来保证线程安全,线程安全带来的负面影响就是会拉低系统性能。


StringBuffer线程安全的实现

String拼接产生垃圾对象,StringBuffer线程安全拉低性能,不考虑线程安全的情况下字符串拼接应该怎么做?StringBuilder闪亮登场,StringBuilder是JDK1.5发布的,它和StringBuffer本质上没什么区别,就是去掉了保证线程安全的那部分,减少了开销,既满足了字符串拼接,又满足了不考虑线程安全的效率问题。


StringBuilder不保证线程安全

StringBuffer 和 StringBuilder 二者都继承了 AbstractStringBuilder ,底层都是利用可修改的char数组(JDK 9 以后是 byte数组)。
所以如果我们有大量的字符串拼接,如果能预知大小的话最好在new StringBuffer 或者StringBuilder 的时候设置好capacity,避免多次扩容的开销。扩容要抛弃原有数组,还要进行数组拷贝创建新的数组。

三兄弟对比

性能对比

前面我们提到了一些性能上的对比,得出三兄弟性能排序:String < StringBuffer < StringBuilder,下面我们通过实际例子来验证一下:

package Test;

public class StringTest {
    // 循环次数,电脑性能有差异,可以根据情况修改该字段
    private static Integer testCount = 100000;

    public static void main(String[] args) {
        StringPerformanceTest();
        StringBuilderPerformanceTest();
        StringBufferPerformanceTest();
    }

    private static void StringPerformanceTest() {
        long start = System.currentTimeMillis();
        String string = "";
        for (int i = 0; i < testCount; i++) {
            string = string + i + ",";
        }
        System.out.println("String拼接时间:" + (System.currentTimeMillis() - start));
    }

    private static void StringBuilderPerformanceTest() {
        long start = System.currentTimeMillis();
        StringBuilder stringBuilder = new StringBuilder("");
        for (int i = 0; i < testCount; i++) {
            stringBuilder.append(i + ",");
        }
        System.out.println("StringBuilder拼接时间:" + (System.currentTimeMillis() - start));
    }


    private static void StringBufferPerformanceTest() {
        long start = System.currentTimeMillis();
        StringBuffer stringBuffer = new StringBuffer("");
        for (int i = 0; i < testCount; i++) {
            stringBuffer.append(i + ",");
        }
        System.out.println("StringBuffer拼接时间:" + (System.currentTimeMillis() - start));
    }
}

执行结果如下:

String拼接时间:56694
StringBuilder拼接时间:16
StringBuffer拼接时间:18

通过上面的代码,我们证实了三兄弟性能排序:String < StringBuffer < StringBuilder,三者的性能差异是因为String字符串是不可变,每次拼接相当于创建新的String对象,所以效率会很低,且很浪费空间;而StringBuffer采用同步块的方式保证线程安全,效率会有所影响;StringBuilder为可变字符序列,且不保证线程安全,所以效率最高。

线程安全

通过文章开始的源码截图我们得知StringBuffer线程安全,StringBuilder不保证线程安全,而String字符串为final类型,讨论它的安全性没有意义。

总结

  1. 在字符串不经常发生变化的业务场景优先使用String(代码更清晰简洁)。如常量的声明,少量的字符串操作(拼接,删除等)。
  2. 在单线程情况下,如有大量的字符串操作情况,应该使用StringBuilder来操作字符串。不能使用String"+"来拼接而是使用,避免产生大量无用的中间对象,耗费空间且执行效率低下(新建对象、回收对象花费大量时间)。如JSON的封装等。
  3. 在多线程情况下,如有大量的字符串操作情况,应该使用StringBuffer。如HTTP参数解析和封装等。

你可能感兴趣的:(String、StringBuffer、StringBuilder三兄弟)