StringBuilder与StringBuffer 这么答一定没错

StringBuilder与StringBuffer

引言

Stringbuffer与StringBuider一直是面试中经常考察的一道题目,之前也经常复习但是常常搞混,但是个人经验通过阅读源码的方式来加深对于题目的理解往往会让你对这两个类记忆更加深刻,同时也可以让你在回答面试问题的时候让面试官对你刮目相看


首先看到两个类图的结构

在这里插入图片描述
在这里插入图片描述
可以看到两个类都实现了Comparable、Serializable接口、AbstractStringBuilder接口

首先看到AbstractStringBuilder

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

    /**
     * The id of the encoding used to encode the bytes in {@code value}.
     */
    byte coder;

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

我们可以看到实际上两个类都是使用byte数组来存储单个字符的
直接来分析append方法

public AbstractStringBuilder append(String str) {
        if (str == null) {
            return appendNull();//判断字符串是否为空
        }
        int len = str.length();
        //字符串的长度
        ensureCapacityInternal(count + len);
        putStringAt(count, str);
        count += len;
        return this;
    }
private void ensureCapacityInternal(int minimumCapacity) {
        // overflow-conscious code
        int oldCapacity = value.length >> coder;
        if (minimumCapacity - oldCapacity > 0) {
            value = Arrays.copyOf(value,
                    newCapacity(minimumCapacity) << coder);
        }
    }
AbstractStringBuilder(int capacity) {
        if (COMPACT_STRINGS) {
            value = new byte[capacity];
            coder = LATIN1;
        } else {
            value = StringUTF16.newBytesFor(capacity);
            coder = UTF16;
        }
    }

首先在初始化AbstractStringBuilder的时候,会传入初始容量。对于StringBuilder与StringBuffer来说默认传入的初始容量都为16,COMPACT_STRING的值是由jvm注入的,与字符串压缩相关。
我们可以看到构造函数实际上是初始化一个字节数组。LATIN的值为1而UTF16的值为2,原因就是LATIN指字符可以完全由byte值容纳的编码,UTF16大概就指一个字符完全需要由两个byte组成的这种编码,所以在ensureCapacityInternal方法里,根据所占字符长度规则来判断是否需要扩容。

int newCapacity = (oldCapacity << 1) + 2;

每次扩容长度为2*原长度+2
调用 Arrays.copyOf 将原数组扩容并拷贝到一个新字节数组中


回答的第一个点: 两个类都是实现了AbstractStringBuilder接口,并且底层使用byte数组存储字符

两者区别

  • StringBuffer是线程安全的,StringBuilder是线程不安全的
    public synchronized int codePointCount(int beginIndex, int endIndex) {
        return super.codePointCount(beginIndex, endIndex);
    }

    /**
     * @throws IndexOutOfBoundsException {@inheritDoc}
     * @since     1.5
     */
    @Override
    public synchronized int offsetByCodePoints(int index, int codePointOffset) {
        return super.offsetByCodePoints(index, codePointOffset);
    }

    /**
     * @throws IndexOutOfBoundsException {@inheritDoc}
     */
    @Override
    public synchronized void getChars(int srcBegin, int srcEnd, char[] dst,
                                      int dstBegin)
    {
        super.getChars(srcBegin, srcEnd, dst, dstBegin);
    }

    /**
     * @throws IndexOutOfBoundsException {@inheritDoc}
     * @see        #length()
     */
    @Override
    public synchronized void setCharAt(int index, char ch) {
        toStringCache = null;
        super.setCharAt(index, ch);
    }

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

我们可以看到StringBuffer的所有公开方法都是采用synchronized关键字来修饰的,这意味着在操作方法的时候必须获取对象锁。虽然在单线程的情况下锁只是轻量级的,但是还是会有获取锁需要的开销,因此在单线程情况下,我们选择采用StringBuilder

  • 字符串缓存
 public final class StringBuffer
    extends AbstractStringBuilder
    implements java.io.Serializable, Comparable<StringBuffer>, CharSequence
{

    /**
     * A cache of the last value returned by toString. Cleared
     * whenever the StringBuffer is modified.
     *
     */
    private transient String toStringCache;

根据英文注释,我们可以知道这是一个每次调用toString方法后的缓存,当每次StringBuffer被修改后清除

public synchronized String toString() {
        if (toStringCache == null) {
            return toStringCache =
                    isLatin1() ? StringLatin1.newString(value, 0, count)
                               : StringUTF16.newString(value, 0, count);
        }
        return new String(toStringCache);
    }

这样在每次调用toString()方法时,不需要每次产生一个新的String对象,而StringBuilder则没有缓存机制

public String toString() {
        // Create a copy, don't share the array
        return isLatin1() ? StringLatin1.newString(value, 0, count)
                          : StringUTF16.newString(value, 0, count);
    }
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

沉默终止

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值