Java:String、StringBuffer和StringBuilder的异同

本文深入解析Java中String、StringBuffer和StringBuilder的区别与联系,探讨它们的底层实现原理,包括不可变性和可变性、线程安全性及效率对比,适合Java开发者深入理解字符串处理。

Java:String、StringBuffer和StringBuilder的异同

Java中与字符串相关的类主要有String、StringBuffer和StringBuilder,他们主要有什么区别,又为什么会这样呢?结合源码来分析一下。

概述

  • String:不可变的字符序列,底层使用char[]存储
  • StringBuffer:可变的字符序列,线程安全,效率低,底层使用char[]存储
  • StringBuilder:可变的字符序列,线程不安全,效率高,底层使用char[]存储

Why?

String
public final class String
    implements java.io.Serializable, Comparable<String>, CharSequence {
    private final char value[];
}

String类被final修饰,不可继承,String对象的字符内容存储在被fianl修饰的字符数组char[]value中,内容不可修改。对String类型进行修改时,都会生成一个新的 String 对象。以replace()方法为例。

public String replace(char oldChar, char newChar) {
        if (oldChar != newChar) {
            int len = value.length;
            int i = -1;
            char[] val = value;

            while (++i < len) {
                if (val[i] == oldChar) {	              //查找第一个要被替换的字符位置
                    break;
                }
            }
            if (i < len) {
                char buf[] = new char[len];               //创建长度相同的字符数组
                for (int j = 0; j < i; j++) {
                    buf[j] = val[j];		              //复制第一个要被替换的字符之前的内容
                }
                while (i < len) {
                    char c = val[i];
                    buf[i] = (c == oldChar) ? newChar : c;//替换旧字符复制不需要替换的字符
                    i++;
                }
                return new String(buf, true);	          //返回新对象
            }
        }
        return this;
    }
StringBuffer和StringBuilder

StringBuffer和StringBuilder都继承自抽象类 AbstractStringBuilder。

abstract class AbstractStringBuilder implements Appendable, CharSequence {
    /**
     * The value is used for character storage.
     */
    char[] value;

    /**
     * The count is the number of characters used.
     */
    
    int count;

    AbstractStringBuilder() {
    }

    AbstractStringBuilder(int capacity) {
        value = new char[capacity];
    }
}

StringBuffer和StringBuilder在初始化时,当不指定容量(capacity)时默认构造一个容量为16的char型数组。当传入一个int型整数时,构造相应长度的数组,当传入一个String字符串时,构造(字符串长度+16)大小的数组。

 String  str="";                         //char[]value=new char[0];
 String  str1="abc";                     //char[]value=new char[]{'a','b','c'};
 
 StringBuffer sb=new StringBuffer();     //char[]value=new char[16];
 StringBuffer sb=new StringBuffer(10);   //char[]value=new char[10];
 StringBuffer sb1=new StringBuffer(str); //char[]value=new char[str.length()+16];

 public StringBuffer() {
        super(16);
 }

 public StringBuffer(int capacity) {
        super(capacity);
 }

 public StringBuffer(String str) {
        super(str.length() + 16);
        append(str);
 }

StringBuffer和StringBuilder都是在原对象的基础上进行修改,但StringBuffer被synchronized修饰线程安全。

 public synchronized StringBuffer append(String str) {
        toStringCache = null;
        super.append(str);
        return this;		//返回原对象
 }
    
 public StringBuilder append(String str) {
        super.append(str);
        return this;	    //返回原对象
 }   

对于String对象来说它的数组长度是固定的,但StringBuffer和StringBuilder的数组长度会随着插入内容而变化。

StringBuffer和StringBuilder在底层实现上大致相同,以StringBuffer为例来查看扩容过程。

 public synchronized StringBuffer append(String str) {
        toStringCache = null;
        super.append(str);				//调用父类方法
        return this;
 }

public AbstractStringBuilder append(String str) {
        if (str == null)
            return appendNull();
        int len = str.length();			      //插入字符串的长度
        ensureCapacityInternal(count + len);  //扩容
        str.getChars(0, len, value, count);	  //将str复制到扩容后的数组中	
        count += len;					      //StringBuffer实际长度,即数组中已经插入元素个数
        return this;
 }

private void ensureCapacityInternal(int minimumCapacity) {
        
        if (minimumCapacity - value.length > 0) { //已满 扩容
            value = Arrays.copyOf(value,
                    newCapacity(minimumCapacity)); 
            //newCapacity(minimumCapacity)确认新数组大小
            //public static char[] copyOf(char[] value, int newLength)
            //创建大小为newLength的数组,复制value数组内容,并返回新数组
        }
 }

private int newCapacity(int minCapacity) {
        
        int newCapacity = (value.length << 1) + 2; //默认变为原来的数组长度的2倍+2
        if (newCapacity - minCapacity < 0) {
            newCapacity = minCapacity;
        }
        return (newCapacity <= 0 || MAX_ARRAY_SIZE - newCapacity < 0)
            ? hugeCapacity(minCapacity)
            : newCapacity;
}

由以上源码可以看出,当容量不足时进行扩容,一般建立长度为原数组2倍+2的新数组,将原有数组内容复制,返回新数组。扩容会影响插入的效率,在初始化时,应该尽可能的指定容量,避免扩容。

效率

由高到低排列 :StringBuilder> StringBuffer>String

public static void main(String[] args) {

        long  startTime = 0L;
        long endTime = 0L;
        String text = "";
        StringBuffer buffer = new StringBuffer("");
        StringBuilder builder = new StringBuilder("");
        //开始对比
        startTime = System.currentTimeMillis();
        for (int i = 0; i < 20000; i++) {
            buffer.append(String.valueOf(i));
        }
        endTime = System.currentTimeMillis();
        System.out.println("StringBuffer的执行时间:" + (endTime - startTime));
        
        startTime = System.currentTimeMillis();
        for (int i = 0; i < 20000; i++) {
            builder.append(String.valueOf(i));
        }
        endTime = System.currentTimeMillis();
        System.out.println("StringBuilder的执行时间:" + (endTime - startTime));
        
        startTime = System.currentTimeMillis();
        for (int i = 0; i < 20000; i++) {
            text = text + i;
        }
        endTime = System.currentTimeMillis();
        System.out.println("String的执行时间:" + (endTime - startTime));
    }

在这里插入图片描述

总结

  1. 处理字符串,底层都由char[]数组存储内容。.

  2. String是不可变字符序列,char型数组被final修饰,不可以修改;
    对String对象的修改实际上是创建一个新对象 。

  3. StringBuilder和StringBuffer是可变字符序列,都继承自AbstractStringBuilder类,修改是在原对象上进行;
    默认创建一个长度为16的char[]数组存储内容,当容量不足时,一般扩容为原数组长度的2倍+2;
    StringBuffer被synchronized修饰,线程安全,效率更低,StringBuilder线程不安全,效率更高;
    当频繁的修改字符串时,在单线程情况下使用StringBuilder,多线程使用StringBuffer。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值