Stringbuilder和String的区别,一文弄清String、StringBuilder、StringBuffer
文章目录
情景再现
今天刷题,有关字符串的操作,采用String的时候,总会出现超出时间限制,当我改成用StringBuilder时时间就会下降很多。
那么同样时间复杂度的算法,为什么这两个操作所耗费的时间相差会这么大呢。
1. 可变性
String修改的底层逻辑
创建 String
对象的过程
-
当你创建一个
String
对象时,例如通过new String("Hello")
,Java 会在堆内存中为这个字符串分配空间。这个空间的大小取决于字符串的实际内容,即字符的数量。 -
String
对象内部使用一个char
类型的数组来存储字符数据。这个数组的长度在创建时就已经确定,等于字符串的长度。
修改 String
对象
-
由于
String
对象是不可变的,任何看似修改String
的操作(如拼接、替换等)都会生成一个新的String
对象。 -
例如,当你执行
str = str + "World";
时,Java 会创建一个新的String
对象,这个新对象包含原始字符串和 “World” 的组合。这个过程涉及到:- 创建一个新的
char
数组,长度为原始字符串长度加上 “World” 的长度。 - 将原始字符串的字符复制到新数组的相应位置。
- 将 “World” 的字符复制到新数组的末尾。
- 返回这个新创建的
String
对象。
- 创建一个新的
-
原来的
String
对象(如果不再被引用)将变得无用,最终会被垃圾回收器回收。
SringBuilder的底层逻辑
创建StringBuilder
的过程
StringBuilder
内部使用一个可变的char
数组来存储字符串数据。这个数组在StringBuilder
对象创建时初始化,并随着字符串内容的修改而动态调整大小。StringBuilder
有两个重要的属性:capacity
(容量)和length
(长度)。capacity
是内部数组能够存储的最大字符数,而length
是当前字符串的实际长度。length
总是小于或等于capacity
。
修改StringBuilder
的过程
- 当你向
StringBuilder
中添加字符,并且当前的capacity
不足以容纳更多的字符时,StringBuilder
会自动进行扩容。扩容通常涉及到创建一个新的更大的数组,并将旧数组中的字符复制到新数组中,然后添加新的字符。 StringBuilder
在扩容时,通常会将容量增加到当前capacity
的两倍,或者如果capacity
很小,可能会增加一个固定的值(如 16)。StringBuilder
提供了多种方法来修改字符串,如append
、insert
、replace
、delete
等。这些方法直接在内部数组上进行操作,而不是创建新的字符串对象。例如,append
方法会检查当前capacity
是否足够,如果不够,会先进行扩容,然后将新字符添加到数组的末尾。
2. 线程安全性:
如何理解线程安全性
当我们的项目使用多线程过程中。比如某一个变量,线程A去修改的同时,线程B也去修改。
因为多线程是并发的,那么在两个线程对这个变量修改之后会出现该变量起数据不一致或者程序运行出错的问题。
线程安全性的重要性
线程安全性在支付场景十分常见,简单的情景再现一下。假设有一个银行账户类 BankAccount
,其中包含一个 balance
属性,表示账户的余额。现在有两个线程 A 和 B,它们都需要从同一个账户中取钱。
class BankAccount {
private double balance;
public BankAccount(double balance) {
this.balance = balance