潘瑞峰的个人博客

java学习笔记 String、StringBuilder、StringBuffer

潘瑞峰 javaStringStringBuilderStringBuffer

常用语法差异

  1. 初始化

    • String : String的初始化可以通过String string = new String("a")String string = "a" 实现,它们是有差别的,在这里就不再阐述了
    • StringBuilder : 只能通过StringBuilder stringBuilder = new StringBuilder("a")初始化
    • StringBuffer : 只能通过StringBuffer stringBuffer = new StringBuffer("a")初始化
  2. 字符串修改

    • String : String是不可变的,从它的定义可见
    public final class String implements java.io.Serializable, Comparable<String>, CharSequence {
         private final char value[];
         //......
    }
    

    所以它每次修改都是创建了一个新的字符串。他的修改过程: 首先创建一个StringBuffer,然后调用StringBuffer的append()方法,最后调用StringBuffer的toString()方法返回

    String s = "hello";
    s += "world";
    //等价于
    StringBuffer sb = new StringBuffer("hello");
    sb.append("world");
    s = sb.toString();
    
    • StringBuilder和StringBuffer : 他们是调用append()方法
    //StringBuffer
    @Override
    public synchronized StringBuffer append(String str) {
        toStringCache = null;
        super.append(str); //它的父类是AbstractStringBuilder
        return this;
    }
    
    //StringBuilder
    @Override
    public StringBuilder append(String str) {
        super.append(str); //它的父类是AbstractStringBuilder
        return this;
    }
    //他们的操作很相似,差异在于StringBuffer做了同步,是线程安全的
    
    

效率差异

我做了一个小实验,分别对String、StringBuffer和StringBuilder修改10000次,查看他们花费的时间。

    public static void main(String[] args) {
        testString();
        testStringBuffer();
        testStringBuilder();
    }
    
    public static void testString() {
        String a = "hello";
        String b = "world";
        long start = System.currentTimeMillis();
        for(int i = 0 ; i < 10000 ; i++) {
            a += b;
        }
        long end = System.currentTimeMillis();
        
        System.out.println("String: " + (end - start));
    }
    
    public static void testStringBuffer() {
        StringBuffer a = new StringBuffer("hello");
        String b = "world";
        long start = System.currentTimeMillis();
        for(int i = 0 ; i < 10000 ; i++) {
            a.append(b);
        }
        long end = System.currentTimeMillis();
        
        System.out.println("StringBuffer: " + (end - start));
    }
    
    public static void testStringBuilder() {
        StringBuilder a = new StringBuilder("hello");
        String b = "world";
        long start = System.currentTimeMillis();
        for(int i = 0 ; i < 10000 ; i++) {
            a.append(b);
        }
        long end = System.currentTimeMillis();
        
        System.out.println("StringBuilder: " + (end - start));  
    }

它的输出结果:

String: 236
StringBuffer: 1
StringBuilder: 1

不难想到,String每次需要创建新的对象,那么它所花费的时间比如是最多的。顺便说一下,这个实验是在2.2 GHz Intel Core i7、16 GB 1600 MHz DDR3的macOS操作系统上做的。

安全差异

  • String是不可变,所以String是线程安全的,因为多个线程同时修改一个String的时候,都是创建了新的字符串
  • StringBuffer是线程安全的,以append()方法为例,它在这个方法上面加了同步,而且它用toStringCache来做缓存,避免了同时读写导致的错误,所以它也是安全的

     @Override
    public synchronized StringBuffer append(String str) {
        toStringCache = null;
        super.append(str);
        return this;
    }
    
    @Override
    public synchronized String toString() {
        if (toStringCache == null) {
            toStringCache = Arrays.copyOfRange(value, 0, count);
        }
        return new String(toStringCache, true);
    }
    
  • StringBuilder不是线程安全的,它没有同步也没有缓存

    @Override
    public StringBuilder append(String str) {
        super.append(str);
        return this;
    }
    

总结

  • 效率: StringBuilder > StringBuffer > String
  • 在使用的时候,如果操作的数据量比较小,应该优先使用String
  • 如果是在单线程下大量操作数据,应该优先使用StringBuilder
  • 如果是在多线程下大量操作数据,应该优先使用StringBuffer
  • 乱入: 一个类只能继承一个类,一个类能实现多个接口,一个接口能继承多个接口
潘瑞峰
五花马,千金裘,呼儿将出换美酒,与尔同销万古愁。