[Java基础]StringBuilder 和 StringBuffer 和 String 比较
String、StringBuffer、StringBuilder的区别
- 可变与不可变:String是不可变字符串对象,StringBuilder和StringBuffer是可变字符串对象(其内部的字符数组长度可变)。
- 是否多线程安全:String中的对象是不可变的,也就可以理解为常量,显然线程安全。StringBuffer 与 StringBuilder 中的方法和功能完全是等价的,只是StringBuffer 中的方法大都采用了synchronized 关键字进行修饰,因此是线程安全的,而 StringBuilder 没有这个修饰,可以被认为是非线程安全的。
- String、StringBuilder、StringBuffer三者的执行效率:StringBuilder > StringBuffer > String 当然这个是相对的,不一定在所有情况下都是这样。比如String str = "hello"+ "world"的效率就比 StringBuilder st = new StringBuilder().append("hello").append("world")要高。因此,这三个类是各有利弊,应当根据不同的情况来进行选择
使用:
- 当字符串相加操作或者改动较少的情况下,建议使用 String str="hello"这种形式;
- 当字符串相加操作较多的情况下,建议使用StringBuilder,如果采用了多线程,则使用StringBuffer。
可变性
-
String 是不可变的。
-
StringBuilder 与 StringBuffer 都继承自 AbstractStringBuilder 类,在 AbstractStringBuilder 中也是使用字符数组保存字符串,不过没有使用 final 和 private 关键字修饰,最关键的是这个 AbstractStringBuilder 类还提供了很多修改字符串的方法。比如 append 方法。从如下代码中可以看到,append方法并不是创建一个新的string对象,而是在原有的string中的char数组中直接进行修改(当然如果数组长度不够了会进行扩容产生一个新的数组)。
abstract class AbstractStringBuilder implements Appendable, CharSequence {
char[] value;
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;
}
//...
}
线程安全性
- String 中的对象是不可变的,也就可以理解为常量,线程安全。
AbstractStringBuilder 是 StringBuilder 与 StringBuffer 的公共父类,定义了一些字符串的基本操作,如 expandCapacity、append、insert、indexOf 等公共方法。
-
StringBuilder 并没有对方法进行加同步锁,所以是非线程安全的。
-
StringBuffer 对方法加了同步锁或者对调用的方法加了同步锁,所以是线程安全的。
性能
- 每次对 String 类型进行改变的时候,都会生成一个新的 String 对象,然后将指针指向新的 String 对象。
- StringBuffer 每次都会对 StringBuffer 对象本身进行操作,而不是生成新的对象并改变对象引用。
- 相同情况下使用 StringBuilder 相比使用 StringBuffer 仅能获得 10%~15% 左右的性能提升,但却要冒多线程不安全的风险。
对于三者使用的总结:
- 操作少量的数据: 适用 String
- 单线程操作字符串缓冲区下操作大量数据: 适用 StringBuilder
- 多线程操作字符串缓冲区下操作大量数据: 适用 StringBuffer
在Java中,String
、StringBuilder
和 StringBuffer
是三个用于处理字符串的类,但它们在特性和用法上有所不同。以下是每个类的详细解释以及它们的区别:
1. String
- 不可变性:
String
对象是不可变的。一旦创建了一个String
对象,其值就不能改变。如果需要修改字符串,则会创建一个新的String
对象。 - 线程安全:由于
String
对象是不可变的,因此是线程安全的。 - 性能:由于每次修改都会创建新的对象,频繁的字符串操作可能会导致性能问题和内存开销的增加。
String str = "Hello";
str += " World"; // 新建了一个字符串对象,str引用了新的"Hello World"对象
2. StringBuilder
- 可变性:
StringBuilder
是可变的,意味着它的内容可以被修改。 - 线程不安全:因为它不是同步的,因此
StringBuilder
是线程不安全的,适用于单线程环境。 - 性能:由于不需要创建新的对象来进行修改,
StringBuilder
在频繁操作字符串时性能表现更好。
StringBuilder sb = new StringBuilder("Hello");
sb.append(" World"); // 修改了同一个StringBuilder对象
3. StringBuffer
- 可变性:
StringBuffer
也是可变的,可以修改其内容。 - 线程安全:
StringBuffer
是线程安全的,因为它的方法是同步的,使用了synchronied关键字,适用于多线程环境。 - 性能:由于使用了同步,即使在单线程环境中,
StringBuffer
的性能可能会比StringBuilder
略差。
StringBuffer sbf = new StringBuffer("Hello");
sbf.append(" World"); // 修改了同一个StringBuffer对象
总结区别
- 不可变性:
String
是不可变的,StringBuilder
和StringBuffer
是可变的。 - 线程安全:
String
是线程安全的,因为它是不可变的;StringBuffer
是线程安全的,因为它的方法是同步的;StringBuilder
是线程不安全的,它的设计目的是用在单线程环境中。 - 性能:
StringBuilder
通常比StringBuffer
性能更好,适用于单线程环境;StringBuffer
适用于需要线程安全的多线程环境;而String
由于不可变性,频繁操作字符串时性能较差。
选择哪个类要根据具体的应用场景和需求来决定。如果在单线程环境中进行大量字符串操作,StringBuilder
是首选;如果在多线程环境中操作字符串,StringBuffer
是优选;而对于完全不可变的字符串,使用 String
是最佳选择。
为什么
StringBuilder
和StringBuffer
可变
在 Java 中,StringBuilder
和 StringBuffer
是可变的字符序列,主要用于在字符串频繁修改(如追加、插入、删除等)时提高性能。它们与 String
类的最大区别在于:String
是不可变的,而 StringBuilder
和 StringBuffer
是可变的。这种可变性通过底层的动态数组和方法操作实现,以下是它们如何实现可变性的详细解释。
1. 底层使用可变的字符数组
StringBuilder
和 StringBuffer
的核心是一个 可变的字符数组(char[]
)。这个数组用于存储当前的字符串内容,当字符串内容改变时,字符数组的内容会被直接修改,而不需要像 String
那样每次修改都创建新的对象。
初始容量和扩展
-
当创建
StringBuilder
或StringBuffer
时,它们会分配一个初始容量的字符数组(通常默认为 16 个字符的容量)。这个初始容量是可以指定的,如果没有指定,默认的容量是 16。 -
当字符数组的容量不足以容纳新的字符时,
StringBuilder
和StringBuffer
会动态扩展数组的大小。扩展的过程是通过 创建一个新的、更大的字符数组,然后将旧数组中的内容复制到新数组中。
例子:
StringBuilder sb = new StringBuilder();
sb.append("Hello");
sb.append(" World");
System.out.println(sb.toString());
- 原理:在上面的代码中,
StringBuilder
的内部字符数组被初始化为默认容量 16。当append()
方法被调用时,字符串 "Hello" 和 "World" 被直接追加到内部的字符数组中,数组的大小自动扩展以适应更多的字符。
2. 可变性实现机制:修改内部数组
StringBuilder
和 StringBuffer
的 append()
、insert()
、delete()
等方法直接操作内部的字符数组,而不是像 String
那样返回一个新的对象。这使得这些操作能够在原地修改字符串内容,避免了频繁创建新对象的开销。
例子:append()
操作
StringBuilder sb = new StringBuilder("Hello");
sb.append(" World"); // 修改了内部的字符数组,而不是创建新的对象
System.out.println(sb.toString()); // 输出 "Hello World"
StringBuilder
的append()
方法会将新字符串的字符添加到内部字符数组的末尾。如果当前数组容量不足,则扩展数组容量,再进行追加操作。
内部实现原理(伪代码简化):
public StringBuilder append(String str) {
if (str == null) {
str = "null";
}
int newCount = count + str.length(); // 计算新字符串需要的总长度
if (newCount > value.length) {
expandCapacity(newCount); // 如果容量不足,扩展数组
}
str.getChars(0, str.length(), value, count); // 将新字符串复制到字符数组
count = newCount; // 更新字符数组的使用长度
return this;
}
expandCapacity(newCount)
:当字符数组容量不足时,StringBuilder
和StringBuffer
会调用此方法扩展数组容量。通常,新容量会比当前容量大约扩大为 当前容量的两倍加上 2,以避免频繁的数组扩展操作。
3. StringBuilder
与 StringBuffer
的区别
虽然 StringBuilder
和 StringBuffer
的底层实现方式非常相似(都是基于可变字符数组的动态扩展),但它们之间有一个重要的区别:
-
StringBuffer
是线程安全的:StringBuffer
的方法是同步的(synchronized
),因此在多线程环境下,多个线程可以安全地对同一个StringBuffer
实例进行操作。每个方法调用都会获取一个锁,保证线程安全。 -
StringBuilder
不是线程安全的:StringBuilder
的方法没有同步,因此在单线程环境下,它的性能比StringBuffer
更高,因为它避免了不必要的锁定开销。StringBuilder
更适合在单线程场景中使用。
4. 可变性与性能的关系
由于 StringBuilder
和 StringBuffer
是可变的,它们的设计大大减少了内存消耗和提升了性能,尤其在大量修改字符串的场景中,例如在循环中拼接字符串。String
的不可变性意味着每次字符串修改都要创建新的对象,而 StringBuilder
和 StringBuffer
则是在原有字符数组上进行修改,从而避免了额外的对象创建。
性能对比:
// String 不可变的操作,每次都会创建新的字符串对象
String str = "";
for (int i = 0; i < 1000; i++) {
str += i; // 由于 String 是不可变的,每次拼接都会创建新的对象
}
// StringBuilder 可变操作,直接在原有字符数组上修改
StringBuilder sb = new StringBuilder();
for (int i = 0; i < 1000; i++) {
sb.append(i); // 直接在同一个对象的字符数组上追加
}
- 在第一个例子中,
String
每次拼接都会创建一个新的字符串对象,效率低且内存消耗高。 - 在第二个例子中,
StringBuilder
直接在原字符数组上修改,性能更高,且内存消耗较少。
5. 总结
StringBuilder
和StringBuffer
的可变性:它们的可变性通过底层的 动态字符数组 实现。在字符串内容改变时,直接在内部数组上进行修改,而不需要创建新的对象。随着内容的增加,字符数组会自动扩展以适应更多字符。StringBuffer
是线程安全的,而StringBuilder
不是:StringBuffer
的方法通过同步机制保证线程安全,但在单线程环境下,StringBuilder
由于不需要同步,性能更好。- 性能优势:由于
StringBuilder
和StringBuffer
可以在同一个对象上进行多次修改,它们在频繁修改字符串时性能显著优于String
。
在开发中,如果需要频繁地修改字符串,建议使用 StringBuilder
(单线程)或 StringBuffer
(多线程),这样可以大幅提高程序的性能。
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· TypeScript + Deepseek 打造卜卦网站:技术与玄学的结合
· 阿里巴巴 QwQ-32B真的超越了 DeepSeek R-1吗?
· 【译】Visual Studio 中新的强大生产力特性
· 【设计模式】告别冗长if-else语句:使用策略模式优化代码结构
· 10年+ .NET Coder 心语 ── 封装的思维:从隐藏、稳定开始理解其本质意义