StringBuffer原理探究

本文深入探讨了Java中StringBuffer的实现原理,包括构造函数、添加字符串时的扩容机制、删除、替换和插入字符串的过程。重点分析了扩容策略,如何避免频繁数组复制,并通过源码解释了关键方法的行为。此外,还提到了toStringCache字段的作用,用于提高效率。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

StringBuffer原理

在这里插入图片描述

可以从StringBuffer源码看出,StringBuffer主要继承于一个抽象类

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

我们看StringBuffer的实现方法,然而并没有什么用,里面的实现大多数是调用了父类的方法了,所以去拜访父类AbstractStringBuilder
在这里插入图片描述

从上图可以看出,AbstractStringBuilder只有两个成员变量,第一个char数组主要是存储真正懂得字符串,第二个是记录已经使用的长度,应为value开辟的空间一般是大于实际保存的,如果char数组的空间不够用的时候,需要用到扩容机制。

下面我们开始探索所有函数

构造函数

在这里插入图片描述

在这里插入图片描述

从构造函数可以看出StringBuffer调用父类AbstractStringBuilder类的构造方法,默认的字符数组长度是16,当然在StringBuffer在构造时候可以穿一个整数指定长度。

添加字符串

在这里插入图片描述
在这里插入图片描述

我们先看append方法的源码,都可以看到在添加的时候,调用了方法ensureCapacityInternal

扩容机制

传入了当前数组长度加上需要添加的长度,也就是添加元素后,字符数组将要改变为多少。所以我们不得不转入这个方法。
在这里插入图片描述

可以看到,他首先检查了前面传入的长度如果大于实际字符数字的长度,说明此时实际的数组长度不够用了,需要将数组长度扩大。

首先补充一下,说下方法copyOf

在这里插入图片描述

这个是Arrays类提供的静态方法,将原来的数组扩充到newLength长度,然后将原来的数据拷贝过去,可以从源码看出,它首先创建出一个新的字符数组,然后使用System的静态方法arraycopy将原来的数据拷贝到新的数组,然后返回新的数组。
在这里插入图片描述

也顺便看看arraycopy方法,他是native方法,所以说没有java实现,他是调用了c++写的方法,所以说为什么不使用for循环一个一个的将字符复制,而是使用了arraycopy的原因是这个方法原则来说快。

说完之后,我们再回到ensureCapacityInternal方法,把上面的图继续放到我们眼前。

在这里插入图片描述

可以看到将扩容后的数组由copyOf返回后,又赋值给了value,然后突然发现到底扩容到多大,是数据传多长,就把他扩容到多大吗,显然不是,如果这样的话,每次添加元素都需要扩容,每次重新分配char数组,分配空间效率大大降低,所以需要一部分备用空间。需要多大的备用空间呢,看方法newCapacity
在这里插入图片描述

首先第一句算出了需要扩充到的空间长度(value.length << 1) + 2 这句话的<< 就是二进制移位计算,左移相当于*2,右移相当于/2,使用移位的话,效率高,最后计算的长度就是原来长度翻倍然后+2(为什么还要加个2我还不明白),

计算出预判的长度后,和需要的长度去比较,如果我扩大2倍多还比需要的短,那就是来捣乱的,一下子增加太多了,那么他要多少就给多少。然后最后返回了一个三元表达式,分析这个三元表达式。

newCapacity <= 0 || MAX_ARRAY_SIZE - newCapacity < 0

这里newCapacity <= 0 就是当传入的minCapacity < -2的负数时候成立

这里MAX_ARRAY_SIZE - newCapacity < 0 当计算出来的长度比MAX_ARRAY_SIZE长度还长就成立,MAX_ARRAY_SIZE在源码可以看到

private static final int MAX_ARRAY_SIZE = Integer.MAX_VALUE - 8;

如果满足上面的条件就,会去找hugeCapacity,不是的话直接返回之前计算的长度。
在这里插入图片描述

可以看出,超过整数范围就跑错,没有的话返回一个三元表达式,分析一下

前面已经判断了minCapacity没有超过整数范围,而MAX_ARRAY_SIZE是最大整数减去8,所以可以得出

Integer.MAX_VALUE - 8 < minCapacity < Integer.MAX_VALUE 返回minCapacity,这里以及是用到了极限了,这次之后再无法扩容了,一扩容就报错。

1 <= minCapacity <= Integer.MAX_VALUE - 8 返回Integer.MAX_VALUE - 8

思考

这里我们就思考了,弄这么多,到底干啥了,其实就是如果newCapacity方法计算的空间如果不是负数或者小于Integer.MAX_VALUE - 8的数,那么就正常走,使用计算的扩容长度,如果newCapacity方法计算的空间如果是负数或者大于Integer.MAX_VALUE - 8的数,那么如果超了整数范围,那么报错,没有的话如果超了Integer.MAX_VALUE - 8,那么使用极限长度,没有超则使用Integer.MAX_VALUE - 8,但是前提是传过来的数肯定超出了,所以这个函数就是使用了极限范围

在最后计算到合适的长度后,扩充容量到所计算的范围。

添加字符串

在这里插入图片描述

继续回到添加元素的方法,首先调用ensureCapacityInternal对容量进行处理,需要扩容的时候扩容。

然后调用字符串的getChars方法,我们看这个方法

在这里插入图片描述

可以看出,前面做了一些异常判断,然后调用字符数组拷贝方法,将str的数据拷贝过去,最后将计数器count+1。

在这里插入图片描述

删除部分字符串

在这里插入图片描述

删除字符串使用的是覆盖数据的方法

原理图
在这里插入图片描述

替换字符串

在这里插入图片描述

替换首先是将原来value在end开始之后的字符往后移动**字符串长度-(end-start)**长度,替换成字符数组拷贝的方法,也就是说将end开始到count-end(这里表示替换位置之后剩余的部分)拷贝到从start+len开始的地方(这里表示给字符串腾出的位置的结尾开始拷贝剩余的字符串)拷贝,然后将需要替换的字符串填充在腾出的空间上。

原理图

在这里插入图片描述

插入字符串

在这里插入图片描述

原理图

在这里插入图片描述

总结

可以看出,删除,替换,插入操作肯定用到了数组的复制,添加可能会数组扩容

回到StringBuffer

在这里插入图片描述

在这里插入图片描述

谈谈toStringCache字段

首先transient说明这个字段无法被序列化

这个主要还是为了保存ToSring执行生成的字符数组,为了提高效率,如果字符串没有改变的情况下,直接返回。
在这里插入图片描述

可以看到每次修改都会将toStringCache字段赋值为空,在调用ToString后就会获取到修改后的字符串,而不会走缓存。

最后附加继承图

在这里插入图片描述

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值