StringBuilder与StringBuffer的底层存储原理

StringBuilder和StringBuffer是 Java 中用于处理字符串的两个类,它们的底层存储原理和使用方法基本类似,但有一些关键的区别。

一、StringBuilder 和 StringBuffer 的共同点

1.可变性:两者都提供了可变的字符串存储,允许动态修改字符串内容,而不需要每次修改都创建一个新的对象。

2.字符数组存储:它们的底层实现都是通过一个字符数组 char[] 来存储字符串内容。

二、主要区别

1.线程安全性:

StringBuilder:非线程安全的。适合在单线程环境下使用,因为其方法没有进行同步,所以效率较高。

StringBuffer:线程安全的。通过在每个方法上加上  synchronized  关键字来保证线程安全,因此可以安全地在多线程环境下使用,但相对于StringBuilder会有一定的性能损耗。

2.性能比较:

在单线程环境下,StringBuildr 的性能优于StringBuffer,因为不需要进行线程同步操作。

在多线程环境下,StringBuffer由于需要进行同步操作,可能会比StringBuilder略慢。

三、底层存储实现

无论是StringBuilder还是StringBuffer,它们的底层存储实现机制基本相同:

1.char[] 数组:用于存储字符串的字符序列。

2.扩容策略:当字符串长度不足以容纳新添加的字符时,会自动扩展数组的大小。通常是通过创建一个新的更大的数组,并将旧数组的内容复制到新数组来实现的。

四、扩容策略

从底层源码角度来说,StringBuilder和StringBuffer的扩容策略确实略有差异,尽管它们的整体思路相似。以下是它们的扩容策略的详细解释。

1.StringBuilder 的扩容策略

StringBuilder的扩容策略可以在 OpenJDK 的源码中找到具体的实现。以下是StringBuilder在 Java 8 版本中的扩容策略:

1.初始容量和最大容量:

(1)StringBuilder的默认初始容量是 16 个字符。

(2)最大容量是Integer.MAX_VALUE - 8,因为需要保留一些空间用于对象头信息和其他一些标记。

下面是一个例子,给StringBuilder对象中添加26个英文字母。

public class Test {
	public static void main(String[] args) {

		StringBuilder sb = new StringBuilder();

		for (char i = 'a'; i < 'z'; i++) {
			// append(),追加元素到末尾。
			sb.append(i);
		}

		System.out.println(sb);
	}

}

当我们用无参构造创建一个StringBuilder对象sb时,发现在StringBuilder类中的无参构造调用了父类AbstractStringBuilder的有参构造(参数为16)。

进入到父类AbstractStringBuilder的的有参构造中,发现将参数16赋给了一个char类型的数组value,这就是StringBuilder对象的默认初始容量(16个字符)。

但是,英文字母有26个,已经超过了StringBuilder的默认初始容量,剩下的元素改如何添加呢?这就涉及到了StringBuilder的扩容操作。 

2.扩容操作:

(1)如果需要扩容,会创建一个新的字符数组,并将原有的字符数组内容复制到新数组中。

(2)扩容操作是通过Arrays.copyOf方法来实现的,这个方法会创建一个新的数组并复制指定长度的内容。

接着,让我们进入到append方法中。
  append方法中调用了ensureCapacityInternal方法,再进入到ensureCapacityInternal方法中。

我们发现,如果所需的最小容量大于value的长度,就进入到if判断内,可以看出扩容操作是通过Arrays.copyOf方法来实现的,这个方法会创建一个新的数组并复制指定长度的内容。

Arrays.copyOf方法第二个参数调用了一个名为newCapacity的方法,传入的参数为所需的最小容量。

让我们再进入到这个newCapacity方法中,可以发现此处定义了一个局部变量newCapacity,它的长度等于value的长度×2在+2,这就是扩容后的StringBuilder对象的容量。

        int newCapacity = (value.length << 1) + 2;

如果所需的最小容量大于扩容后的容量,那么再将最小容量赋给扩容后的容量。

最后再返回局部变量newCapacity,这就是最终扩容后的StringBuilder对象的容量。

这里,我们也可以看到StringBuilder的最大容量是Integer.MAX_VALUE - 8。

 3.性能考虑:

(1)扩容操作的性能是可预测的,因为每次扩容都是按照固定的公式进行的,不会出现不必要的内存浪费。 

2.StringBuffer 的扩容策略

StringBuffer的扩容策略与StringBuilder类似,但有一个主要区别在于线程安全的考虑:

1.初始容量和最大容量:

(1)StringBuffer的默认初始容量也是 16 个字符。

(2)最大容量也是Integer.MAX_VALUE - 8,与StringBuilder相同。

依旧是上面那个例子,不过这次创建的是StringBuffer的对象。

public class Test {
	public static void main(String[] args) {

		StringBuffer sb = new StringBuffer();

		for (char i = 'a'; i < 'z'; i++) {
			// append(),追加元素到末尾。
			sb.append(i);
		}

		System.out.println(sb);
	}

}

首先调用StringBuffer的无参构造创建一个对象,在Stringbuffer无参构造中调用了父类AbstractStringBuilder的有参构造,传入参数16。

 进入到父类AbstractStringBuilder的有参构造中可以看到将参数16赋给了一个char类型的数组value,这就是StringBuffer对象的默认初始容量(16个字符)。

同样的,当我们所需的最小容量超过StringBuffer的默认容量16个字符时,StringBuffer将实行扩容操作。 

2.扩容操作:

(1)与StringBuilder类似,如果需要扩容,会创建一个新的字符数组,并将原有的字符数组内容复制到新数组中。

接下来让我们进入到append方法中,可以发现append方法被synchronized关键字来修饰,这就是StringBuffer的线程安全处理。

线程安全处理:StringBuffer在进行扩容时,使用了synchronized关键字来保证线程安全,确保在多线程环境下不会出现竞态条件。

后面的操作就和StringBuilder相同了,这里不做过多介绍。

 newCapacity方法中定义了一个局部变量newCapacity,它的长度等于value的长度×2在+2,这就是扩容后的StringBufferr对象的容量。

        int newCapacity = (value.length << 1) + 2;

如果所需的最小容量大于扩容后的容量,那么再将最小容量赋给扩容后的容量。

最后再返回局部变量newCapacity,这就是最终扩容后的StringBuffer对象的容量。

这里可以看到StringBuffer的最大容量也是Integer.MAX_VALUE - 8。

3.性能考虑:

(1)由于涉及线程同步,StringBuffer在多线程环境下的性能可能略低于StringBuilder,因为需要考虑线程安全带来的额外开销。

补充:Integer.MAX_VALUE的值为:

3.总结

StringBuilder和StringBuffer的扩容策略都是通过动态调整字符数组的大小来实现的,保证了字符串操作的高效性和可扩展性。理解它们的扩容机制有助于我们编写出性能更优的字符串处理代码,在应对不同需求时选择合适的类进行使用。

这种设计保证了在大部分情况下,字符串操作的性能是高效的,因为不需要频繁地重新分配和复制数组,而只在必要时进行扩容操作。

五、综上

综上所述,StringBuilder和StringBuffer虽然在线程安全性和性能上有所差异,但它们的底层存储原理和扩容策略基本相同,都是通过字符数组来存储字符串,并且通过动态扩展数组来支持字符串的动态修改。

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值