java开发:StringBuilder 、StringBuffer源码解析

引言:

String、StringBuilder 、StringBuffer在面试中被问到的几率是非常多的。我们常常这样回答:

1.String是不可变的对象
2.StringBuffer是线程安全的,StringBuilder 不是线程安全的
3.StringBuilder 效率高于StringBuffer

如果面试官继续问StringBuffer、StringBuilder 它们底层是怎么实现字符串拼接的,为什么说StringBuffer是线程安全的?如果你没有了解它们底层源码肯定是回答不上。因此这篇文章我们来看看源码。

StringBuilder 源码:

public final class StringBuilder
    extends AbstractStringBuilder
    implements java.io.Serializable, CharSequence
{

    static final long serialVersionUID = 4383685877147921099L;

 	//调用父类的构造函数,创建一个长度为16的字符数组
    public StringBuilder() {
        super(16);
    }

 	//调用父类的构造函数,创建一个指定长度的字符数组
    public StringBuilder(int capacity) {
        super(capacity);
    }

   	//调用父类的构造函数,传入初始值。构建一个16+初始值长度的数组
    public StringBuilder(String str) {
        super(str.length() + 16);
        append(str);
    }

 
    public StringBuilder(CharSequence seq) {
        this(seq.length() + 16);
        append(seq);
    }

	@Override
    public StringBuilder append(Object obj) {
        return append(String.valueOf(obj));
    }

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


    public StringBuilder append(StringBuffer sb) {
        super.append(sb);
        return this;
    }

    @Override
    public StringBuilder append(CharSequence s) {
        super.append(s);
        return this;
    }

    /**
     * @throws     IndexOutOfBoundsException {@inheritDoc}
     */
    @Override
    public StringBuilder append(CharSequence s, int start, int end) {
        super.append(s, start, end);
        return this;
    }

    @Override
    public StringBuilder append(char[] str) {
        super.append(str);
        return this;
    }

    /**
     * @throws IndexOutOfBoundsException {@inheritDoc}
     */
    @Override
    public StringBuilder append(char[] str, int offset, int len) {
        super.append(str, offset, len);
        return this;
    }

    @Override
    public StringBuilder append(boolean b) {
        super.append(b);
        return this;
    }

    @Override
    public StringBuilder append(char c) {
        super.append(c);
        return this;
    }

    @Override
    public StringBuilder append(int i) {
        super.append(i);
        return this;
    }

    @Override
    public StringBuilder append(long lng) {
        super.append(lng);
        return this;
    }

    @Override
    public StringBuilder append(float f) {
        super.append(f);
        return this;
    }

    @Override
    public StringBuilder append(double d) {
        super.append(d);
        return this;
    }
    }

1.首先你可以看到StringBuilder是final类,是不可被继承的,同时它继承AbstractStringBuilder
2.它的构造函数中都是调用父类AbstractStringBuilder的构造函数。
3.提供了很多append()方法给我们,都是调用父类的append()。

AbstractStringBuilder源码:

abstract class AbstractStringBuilder implements Appendable, CharSequence {
  	//AbstractStringBuilder 内部维护着一个char数组
    char[] value;
    //count值记录数组的元素个数
    int count;
    
    AbstractStringBuilder() {
    }

    //构造函数初始化value数组
    AbstractStringBuilder(int capacity) {
        value = new char[capacity];
    }

    //length()方法返回的是数组的元素个数(不是value的长度)
    @Override
    public int length() {
        return count;
    }

从上面你可以看到AbstractStringBuilder 主要是维护着一个数组,构造函数初始化value数组,子类传过来的长度就是value的长度。

 public AbstractStringBuilder append(String str) {
        if (str == null)
            return appendNull();
         //获取字符串长度
        int len = str.length();
        //判断是否需要扩容
        ensureCapacityInternal(count + len);
        //把字符串复制到value数组
        //getChars()四个参数:字符串中要复制的第一个字符的索引、字符串中要复制的最后一个字符之后的索引、目标数组、目标数组中的起始偏移量
        str.getChars(0, len, value, count);
        //记录数组的元素个数
        count += len;
        return this;
    }
    
    
  private void ensureCapacityInternal(int minimumCapacity) {
  		//minimumCapacity=元素个数+添加的字符串长度,判断数组容量是否足够
        if (minimumCapacity - value.length > 0)
        	//容量不够则进行扩容
            expandCapacity(minimumCapacity);
    }

   void expandCapacity(int minimumCapacity) {
   		//计算扩容后的长度(每次扩容是原来的长度俩倍+2)
        int newCapacity = value.length * 2 + 2;
        //如果扩容后还是不够,直接使用minimumCapacity值作为扩容后的长度
        if (newCapacity - minimumCapacity < 0)
            newCapacity = minimumCapacity;
        if (newCapacity < 0) {
            if (minimumCapacity < 0) // overflow
                throw new OutOfMemoryError();
            newCapacity = Integer.MAX_VALUE;
        }
        //在value基础上创建一个长度为newCapacity的数组(即在原有数组上进行扩容)
        value = Arrays.copyOf(value, newCapacity);
    }

append()方法内部主要调用ensureCapacityInternal()是否需要扩容,是的话就把value扩大到原来的2倍+2。最后调用 str.getChars(0, len, value, count)将字符串复制到value数组。

public AbstractStringBuilder delete(int start, int end) {
		//start 小于0  抛出异常
        if (start < 0)
            throw new StringIndexOutOfBoundsException(start);
        //start 大于count  抛出异常
        if (end > count)
        //结束索引默认最大只能为元素个数
            end = count;
        //起始索引大于结束索引也抛异常
        if (start > end)
            throw new StringIndexOutOfBoundsException();
        int len = end - start;
        if (len > 0) {
        
        	//    arraycopy()可以将源数组复制到目标数组
			//    参数1. 原数组(要拷贝的数组)
			//    参数2. 要复制的源数组的起始位置下标
			//    参数3.目标数组
			//    参数4.复制到目标数组的起始位置下标
			//    参数5.复制长度
            System.arraycopy(value, start+len, value, start, count-end);
            //修改元素个数
            count -= len;
        }
        return this;
    }

delete()方法首先是对传入的参数进行各种判断:
1.开始索引不能小于0,也不能超过count,且不能大于结束索引
2.结束索引可以随便传,但是最大只能是count
最后就是调用 System.arraycopy()进行删除操作,详细用法可以参考我的博客
java开发:集合(一):数组扩容和元素删除

 public AbstractStringBuilder replace(int start, int end, String str) {
        if (start < 0)
            throw new StringIndexOutOfBoundsException(start);
        if (start > count)
            throw new StringIndexOutOfBoundsException("start > length()");
        if (start > end)
            throw new StringIndexOutOfBoundsException("start > end");
        if (end > count)
            end = count;
        int len = str.length();
        //重新计算数组需要的长度(因为str长度可能大于要替换的长度)
        int newCount = count + len - (end - start);
        //判断是否需要扩容
        ensureCapacityInternal(newCount);
		//移动数组的元素留出空间
        System.arraycopy(value, end, value, start + len, count - end);
        //把str复制到value数组
        str.getChars(value, start);
        count = newCount;
        return this;
    }

replace()同样是对参数进行各种判断,ensureCapacityInternal()判断是否需要扩容。最后先调整数组,再把字符串复制到value数组完成替换。

StringBuffer源码:

 public final class StringBuffer
    extends AbstractStringBuilder
    implements java.io.Serializable, CharSequence
{

 
    private transient char[] toStringCache;

    static final long serialVersionUID = 3388685877147921107L;

    public StringBuffer() {
        super(16);
    }

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

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

    public StringBuffer(CharSequence seq) {
        this(seq.length() + 16);
        append(seq);
    }

StringBuffer同样也是继承AbstractStringBuilder 类

 @Override
    public synchronized StringBuffer append(Object obj) {
        toStringCache = null;
        super.append(String.valueOf(obj));
        return this;
    }

    @Override
    public synchronized StringBuffer append(String str) {
        toStringCache = null;
        super.append(str);
        return this;
    }

    public synchronized StringBuffer append(StringBuffer sb) {
        toStringCache = null;
        super.append(sb);
        return this;
    }
    @Override
    synchronized StringBuffer append(AbstractStringBuilder asb) {
        toStringCache = null;
        super.append(asb);
        return this;
    }

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

StringBuffer的相应api都是使用了synchronized 关键字实现同步锁,因此StringBuffer是线程安全的

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值