StringBuffer 和 StringBuilder 的区别 - 源码解析、内容扩展

概述

阅读 Java 版本为 1.8.0.25

先看 StringBufferStringBuilder 的继承关系图。

在这里插入图片描述

StringBufferStringBuilder 都继承了 AbstractStringBuilder ,都是通过 char[] 数组实现,因为未加 final 修饰符,所以可修改。

两者简单来说:

  • StringBuffer :是一个线程安全类,方法使用 synchronized 修饰加锁,执行速度稍微慢点。
  • StringBuilder :线程不安全,执行速度快。单线程情况下,拼接次数多使用。
  • String :少量字符串可以直接使用 + 拼接。

设计目的

在拼接字符串的时候,我们先需要创建字符串 String

String 也是一个对象,它的底层是一个 final 的数组:

private final char value[];

所以,String 是不可变的,每次操作一个字符串都会创建一个新的字符串,大大影响了运行效率。

例如以下示例中:

   public static void main(String[] args) {
        long begin = System.currentTimeMillis();
        String s = "";
        for (int i = 0; i < 100000; i++) {
            s +=  i;
        }
        System.out.println(System.currentTimeMillis() - begin);

        begin = System.currentTimeMillis();
        StringBuilder sb = new StringBuilder();
        for (int i = 0; i < 100000; i++) {
            sb.append(i);
        }
        System.out.println(System.currentTimeMillis() - begin);
    }

运行时间如下:

24504
2

这简直差的不是一点半点啊。。。

所以这就是StringBufferStringBuilder 的设计目的,它的效率确实要比 + 拼接高多了。

在这里插入图片描述

PS:在连续的确定字符串拼接中,编译器会帮你优化。

例如以下拼接:

    public static void main(String[] args) {
        String last = "http://" + "shiva.show" + "/test?params=666";
        System.out.println(last);
    }

在 class 文件中,是这样的:

    public static void main(String[] args) {
        String last = "http://shiva.show/test?params=666";
        System.out.println(last);
    }

当然,这种情况确实写在一起就行了,也没必要再拼接。。。。

如果中间有个变量,那编译器就优化不了了。


实现原理

其实我们在使用 StringBufferStringBuilder 时,基本都是这种模式:

StringBuilder sb = new StringBuilder();
sb.append("http://").append(host)
    .append(":").append(port)
    .append("/").append(name)
    .append("params=").append(params);
return sb.toString();

从这段代码来看:


构造方法

StringBufferStringBuilder 都继承自 AbstractStringBuilder ,并且没有对构造方法进行太多改造。

    public StringBuilder() {
        super(16);
    }

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

默认数据长度为16。

对已知较长的字符串拼接可以提前设置较大的初始长度,原因下面就讲。


数组扩容

在 append() 方法中,如果长度超出数组长度,将进行数组扩容:

    public AbstractStringBuilder append(String str) {
        if (str == null)
            return appendNull();
        int len = str.length();
        ensureCapacityInternal(count + len);
        str.getChars(0, len, value, count);
        count += len;
        return this;
    }
    void expandCapacity(int minimumCapacity) {
        int newCapacity = value.length * 2 + 2;
        if (newCapacity - minimumCapacity < 0)
            newCapacity = minimumCapacity;
        if (newCapacity < 0) {
            if (minimumCapacity < 0) // overflow
                throw new OutOfMemoryError();
            newCapacity = Integer.MAX_VALUE;
        }
        value = Arrays.copyOf(value, newCapacity);
    }

数组扩容为双倍扩容,扩容会进行数组复制,所以也会进行资源消耗。因此,构造时可以事先确定一个较长的数组。

当然,这点消耗简直可以忽略。。。。


StringBuffer 粗锁化

首先来说下,啥是粗锁化。

Hotspot 确实进行了锁粗化优化,可以有效合并几个相邻同步块,从而降低锁开销。能够把下面的代码:

synchronized (obj) {
  // 语句 1
}
synchronized (obj) {
  // 语句 2
}

转化为:

synchronized (obj) {
  // 语句 1
  // 语句 2
}

And Then。。。意思就是,把上面的代码一改:

StringBuffer sb = new StringBuffer();
sb.append("http://").append(host)
    .append(":").append(port)
    .append("/").append(name)
    .append("params=").append(params);
return sb.toString();

中间一段 synchronized 会进行粗锁化,合并为一个大的同步块,这样就减少了加锁解锁的次数,有效地提升了代码执行的效率

呃呃呃呃呃呃

也就是说,其实性能差距并没有想象中那么大。

在这里插入图片描述


扩展内容

Java9 改进了字符串(包括String、StringBuffer、StringBuilder)的实现。

在 Java9 以前字符串采用 char[] 数组来保存字符,因此字符串的每个字符占2字节;而 Java9 的字符串采用 byte[]数组再加一个 encoding-flag 字段来保存字符,因此字符串的每个字符只占1字节。

所以 Java9 的字符串更加节省空间,字符串的功能方法也没有受到影响。

这些是看到的别的博客,公司一直用的 JDK 8,我也就没看过更高版本。


参考文章

https://blog.csdn.net/weixin_41101173/article/details/79677982

https://blog.csdn.net/csxypr/article/details/92378336

https://blog.csdn.net/qq_28303495/article/details/89431042

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值