> 技术文档 > 【Java】StringBuffer与StringBuilder详解_java stringbuilder

【Java】StringBuffer与StringBuilder详解_java stringbuilder

目录

一、基本概念

1.核心区别

2.底层实现

3.StringBuffer与StringBuilder内部实现与扩容机制

⑴核心源码结构

⑵底层数据结构

⑶扩容机制

⑷初始化容量

 ⑸添加字符的流程(以 append(String str) 为例)

⑹ 扩容示例

二、StringBuffer和StringBuilder构造方法

1. 无参构造方法

2. 带初始容量参数的构造方法

3. 带字符串参数的构造方法

4. 带 CharSequence 参数的构造方法(Java 5 及以上)

三、常用方法

1. 追加方法

2. 删除方法

3. 插入方法

4. 替换方法

5. 反转方法

6. 设置方法

7. 获取字符方法

8. 查找方法

9. 长度和子字符串方法

10. 转换为字符串方法

四、String、StringBuilder 和 StringBuffer 的效率对比和优化策略的详细解析

1. 问题根源:String 的不可变性

2. StringBuilder 的高效性

3. StringBuffer 的线程安全代价

4. 性能优化策略

(1) 预分配容量

(2) 避免隐式转换

5. 效率对比总结

6. 实际测试数据

7. 总结与建议


一、基本概念

String类:

String类是不可变的,一旦创建了一个String对象,它的值就不能被改变。如果对String对象进行拼接、替换等操作,实际上是创建了一个新的String对象。

StringBuffer类:

StringBuffer是线程安全的可变字符序列。它的所有公共方法都是同步的,这意味着在多线程环境下可以安全地使用,但在单线程环境下会有一定的性能开销。

StringBuilder类:

StringBuilder是StringBuffer的非线程安全版本,从 Java 5 开始引入。它的方法没有进行同步处理,因此在单线程环境下性能比StringBuffer更高。

在Java中,StringBuffer和StringBuilder是专为频繁字符串拼接设计的可变字符串类,其核心区别和实现细节如下:

1.核心区别

线程安全:

①StringBuffer是线程安全的(方法使用synchronized修饰),适用于多线程环境。

②StringBuilder非线程安全,单线程下效率更高。

出现时间:

StringBuffer自Java 1.0引入,StringBuilder于Java 5新增。

2.底层实现

Java 8及之前:底层使用char[ ]存储字符。

Java 9及之后:优化为byte[ ],根据字符编码动态选择Latin-1(1字节/字符)或UTF-16(2字节/字符),节省内存。

3.StringBuffer与StringBuilder内部实现与扩容机制

在Java中,StringBuilder和StringBuffer的核心实现逻辑继承自同一个父类 AbstractStringBuilder,两者的区别仅在于线程安全性(StringBuffer的方法用synchronized修饰)。

⑴核心源码结构

①继承关系

StringBuilder 和 StringBuffer 均继承自 AbstractStringBuilder

// JDK 17 源码public final class StringBuilder extends AbstractStringBuilder { // 方法未同步}public final class StringBuffer extends AbstractStringBuilder { // 方法用 synchronized 修饰}

AbstractStringBuilder:包含核心的字符数组存储(byte[ ] value)、扩容逻辑和操作方法(append(), insert()等)。

② 扩容代码位置

扩容方法定义在 AbstractStringBuilder 中,StringBuilder 和 StringBuffer 直接继承这些方法。 例如 ensureCapacityInternal(), newCapacity() 等扩容逻辑,两者完全共用同一份实现。

它们的底层数据结构、扩容机制完全一致。以下是基于 JDK 17 源码的深入解析:

⑵底层数据结构

两者均通过 动态字符数组 存储数据,继承自 AbstractStringBuilder,核心字段如下:

// AbstractStringBuilder 类源码abstract class AbstractStringBuilder { byte[] value; // JDK 9 后改为 byte[](优化内存,支持压缩字符串) int count; // 当前实际字符数(非数组长度) // ...}

value:存储字符的数组,初始容量由构造器决定。

count:记录当前实际存储的字符数(即字符串长度)。

JDK 9+优化:从 char[ ] 改为 byte[ ],结合编码标志(coder)压缩存储(LATIN1或UTF16),节省内存。

⑶扩容机制

当添加字符导致 count + 1 > value.length 时触发扩容。核心方法是 ensureCapacityInternal(int minimumCapacity)

源码分析(JDK 17)

/** * 确保内部数组的容量足够,如果当前容量不足,则触发扩容操作。 * * @param minimumCapacity 所需的最小容量 */private void ensureCapacityInternal(int minimumCapacity) { // 当前容量不足时触发扩容 // minimumCapacity 为所需的最小容量,value.length 为当前数组的容量 // 若所需最小容量大于当前数组容量,则需要进行扩容 if (minimumCapacity - value.length > 0) { // 调用 newCapacity 方法计算新的容量,并使用 Arrays.copyOf 方法将原数组复制到新容量的数组中 value = Arrays.copyOf(value, newCapacity(minimumCapacity)); }}/** * 计算新的数组容量。 * * @param minCapacity 所需的最小容量 * @return 计算得到的新容量 */private int newCapacity(int minCapacity) { // 获取当前数组的容量 int oldCapacity = value.length; // 默认扩容策略:旧容量 * 2 + 2 // 使用左移操作符 << 实现乘以 2 的效果,效率更高 int newCapacity = (oldCapacity << 1) + 2; // 若扩容后的容量仍小于所需的最小容量,则直接使用所需的最小容量作为新容量 if (newCapacity - minCapacity < 0) { newCapacity = minCapacity; } // 检查计算得到的新容量是否合法,是否超过最大容量(MAX_ARRAY_SIZE,通常为 Integer.MAX_VALUE - 8) // 如果新容量小于等于 0 或者超过了最大容量限制,则调用 hugeCapacity 方法处理 return (newCapacity <= 0 || MAX_ARRAY_SIZE - newCapacity < 0) ? hugeCapacity(minCapacity) : newCapacity;}/** * 处理大容量需求的情况。 * * @param minCapacity 所需的最小容量 * @return 最终确定的大容量 * @throws OutOfMemoryError 如果所需容量超过了 Integer.MAX_VALUE,抛出内存溢出异常 */private int hugeCapacity(int minCapacity) { // 检查所需的最小容量是否超过了 Integer.MAX_VALUE,如果超过则会发生溢出 if (Integer.MAX_VALUE - minCapacity  MAX_ARRAY_SIZE) ? minCapacity : MAX_ARRAY_SIZE;}

扩容规则

默认扩容策略:新容量 = 旧容量 * 2 + 2。

特殊场景:若所需容量(minCapacity)超过默认扩容后的值,直接使用 minCapacity。

容量上限:最大为 Integer.MAX_VALUE - 8(MAX_ARRAY_SIZE),超过则可能抛出 OutOfMemoryError

⑷初始化容量

不同构造器初始化容量方式不同:

①无参构造器

// StringBuilder 无参构造器public StringBuilder() { super(16); // 调用 AbstractStringBuilder(int capacity)}// AbstractStringBuilder 初始化AbstractStringBuilder(int capacity) { value = (capacity == 0) ? new byte[0] : new byte[capacity]; coder = (byte)(String.COMPACT_STRINGS ? String.LATIN1 : String.UTF16);}
  • 默认容量为16(若未指定)。

 ②指定初始容量

public StringBuilder(int capacity) { super(capacity);}

 ③基于字符串初始化

public StringBuilder(String str) { super(str.length() + 16); // 初始容量 = 字符串长度 + 16 append(str);}

 ⑸添加字符的流程(以 append(String str) 为例)

public AbstractStringBuilder append(String str) { if (str == null) { return appendNull(); // 处理 null } int len = str.length(); ensureCapacityInternal(count + len); // 检查容量 putStringAt(count, str);  // 将字符串复制到 value 数组 count += len; // 更新字符数 return this;}

容量检查:调用 ensureCapacityInternal,若不足则扩容。

数据复制:将新字符串内容复制到 value 数组中。

更新长度:count 增加新字符数。

⑹ 扩容示例

假设初始容量为 5,依次添加字符串 \"HelloWorld\":

操作 当前容量 扩容逻辑 新容量 初始容量 5 - 5 添加 \"Hello\" (5字符) 5 → 不足 新容量 = 5*2 + 2 = 12 12 继续添加 \"World\" (5) 12 - 10 = 2 剩余容量足够,无需扩容 12 再添加 \"!\" (1) 12 - 11 = 1 剩余容量足够,无需扩容 12

⑺操作方法的差异

StringBuilder 的方法:直接调用父类方法,无同步。

// StringBuilder 的 append 方法@Overridepublic StringBuilder append(String str) { super.append(str); // 调用 AbstractStringBuilder.append() return this;}

StringBuffer 的方法:通过 synchronized 保证线程安全。

// StringBuffer 的 append 方法@Overridepublic synchronized StringBuffer append(String str) { toStringCache = null; // JDK 17 前存在,现已移除 super.append(str); // 同样调用 AbstractStringBuilder.append() return this;}
维度 StringBuilder StringBuffer 线程安全 非线程安全 线程安全(方法用synchronized扩容机制 与StringBuffer完全相同 与StringBuilder完全相同 性能 高(无同步开销) 较低(同步开销) 底层存储 共享AbstractStringBuilder的byte[ ] value 同上

二、StringBuffer和StringBuilder构造方法

1. 无参构造方法

功能:创建一个初始容量为 16 个字符的空对象。当存储的字符数量超过 16 时,会自动进行扩容。

示例代码:

public class ConstructorExample { public static void main(String[] args) { // 使用无参构造方法创建 StringBuffer 对象 StringBuffer stringBuffer = new StringBuffer(); // 使用无参构造方法创建 StringBuilder 对象 StringBuilder stringBuilder = new StringBuilder(); System.out.println(\"StringBuffer 初始长度: \" + stringBuffer.length()); System.out.println(\"StringBuilder 初始长度: \" + stringBuilder.length()); }}

运行结果:

2. 带初始容量参数的构造方法

功能:创建一个指定初始容量的空对象。当需要处理大量字符且大致知道所需容量时,使用此构造方法可以避免多次扩容带来的性能开销。

public class ConstructorExample { public static void main(String[] args) { // 创建一个初始容量为 32 的 StringBuffer 对象 StringBuffer stringBuffer = new StringBuffer(32); // 创建一个初始容量为 32 的 StringBuilder 对象 StringBuilder stringBuilder = new StringBuilder(32); System.out.println(\"StringBuffer 初始容量: \" + stringBuffer.capacity()); System.out.println(\"StringBuilder 初始容量: \" + stringBuilder.capacity()); }}

运行结果:

3. 带字符串参数的构造方法

功能:创建一个包含指定字符串内容的对象,初始容量为字符串的长度加上 16。

public class ConstructorExample { public static void main(String[] args) { String str = \"Hello\"; // 创建一个包含指定字符串的 StringBuffer 对象 StringBuffer stringBuffer = new StringBuffer(str); // 创建一个包含指定字符串的 StringBuilder 对象 StringBuilder stringBuilder = new StringBuilder(str); System.out.println(\"StringBuffer 内容: \" + stringBuffer.toString()); System.out.println(\"StringBuilder 内容: \" + stringBuilder.toString()); System.out.println(\"StringBuffer 容量: \" + stringBuffer.capacity()); System.out.println(\"StringBuilder 容量: \" + stringBuilder.capacity()); }}

运行结果:

4. 带 CharSequence 参数的构造方法(Java 5 及以上)

功能:创建一个包含指定 CharSequence 内容的对象。CharSequence 是一个接口,String、StringBuffer、StringBuilder 等类都实现了该接口。

public class ConstructorExample { public static void main(String[] args) { CharSequence charSequence = \"World\"; // 创建一个包含指定 CharSequence 的 StringBuffer 对象 StringBuffer stringBuffer = new StringBuffer(charSequence); // 创建一个包含指定 CharSequence 的 StringBuilder 对象 StringBuilder stringBuilder = new StringBuilder(charSequence); System.out.println(\"StringBuffer 内容: \" + stringBuffer.toString()); System.out.println(\"StringBuilder 内容: \" + stringBuilder.toString()); }}

运行结果:

三、常用方法


1. 追加方法

append(Type data)

语法StringBuffer append(Type data) StringBuilder append(Type data)

功能:将指定类型的数据追加到当前对象的末尾。Type 可以是 String、char、int、double 等多种类型。

示例:

public class AppendExample { public static void main(String[] args) { // StringBuffer append StringBuffer stringBuffer = new StringBuffer(); stringBuffer.append(\"Hello\").append(\" \").append(\"World\"); System.out.println(\"StringBuffer append 结果: \" + stringBuffer.toString()); // StringBuilder append StringBuilder stringBuilder = new StringBuilder(); stringBuilder.append(123).append(true); System.out.println(\"StringBuilder append 结果: \" + stringBuilder.toString()); }}

运行结果:


2. 删除方法

delete(int start, int end)

语法StringBuffer delete(int start, int end)StringBuilder delete(int start, int end)

功能:移除从索引 start(包含)到索引 end(不包含)之间的字符序列。

示例:

public class DeleteExample { public static void main(String[] args) { // StringBuffer delete StringBuffer stringBuffer = new StringBuffer(\"HelloWorld\"); stringBuffer.delete(5, 10); System.out.println(\"StringBuffer delete 结果: \" + stringBuffer.toString()); // StringBuilder delete StringBuilder stringBuilder = new StringBuilder(\"HelloWorld\"); stringBuilder.delete(5, 10); System.out.println(\"StringBuilder delete 结果: \" + stringBuilder.toString()); }}

运行结果:


deleteCharAt(int index)

语法:StringBuffer deleteCharAt(int index) StringBuilder deleteCharAt(int index)

功能:移除指定索引位置的字符。

示例:

public class DeleteCharAtExample { public static void main(String[] args) { // StringBuffer deleteCharAt StringBuffer stringBuffer = new StringBuffer(\"Hello\"); stringBuffer.deleteCharAt(1); System.out.println(\"StringBuffer deleteCharAt 结果: \" + stringBuffer.toString()); // StringBuilder deleteCharAt StringBuilder stringBuilder = new StringBuilder(\"Hello\"); stringBuilder.deleteCharAt(1); System.out.println(\"StringBuilder deleteCharAt 结果: \" + stringBuilder.toString()); }}

运行结果:


3. 插入方法

insert(int offset, String str)

语法:StringBuffer insert(int offset, String str) StringBuilder insert(int offset, String str)

功能:在指定的偏移量 offset 处插入指定的字符串 str。

示例:

public class InsertExample { public static void main(String[] args) { // StringBuffer insert StringBuffer stringBuffer = new StringBuffer(\"Hello\"); stringBuffer.insert(5, \" World\"); System.out.println(\"StringBuffer insert 结果: \" + stringBuffer.toString()); // StringBuilder insert StringBuilder stringBuilder = new StringBuilder(\"Hello\"); stringBuilder.insert(5, \" World\"); System.out.println(\"StringBuilder insert 结果: \" + stringBuilder.toString()); }}

运行结果:


4. 替换方法

replace(int start, int end, String str)

语法StringBuffer replace(int start, int end, String str)StringBuilder replace(int start, int end, String str)

功能:将从索引 start(包含)到索引 end(不包含)之间的字符序列替换为指定的字符串 str。

示例:

public class ReplaceExample { public static void main(String[] args) { // StringBuffer replace StringBuffer stringBuffer = new StringBuffer(\"HelloWorld\"); stringBuffer.replace(5, 10, \" Java\"); System.out.println(\"StringBuffer replace 结果: \" + stringBuffer.toString()); // StringBuilder replace StringBuilder stringBuilder = new StringBuilder(\"HelloWorld\"); stringBuilder.replace(5, 10, \" Java\"); System.out.println(\"StringBuilder replace 结果: \" + stringBuilder.toString()); }}

运行结果:


5. 反转方法

reverse()

语法:StringBuffer reverse()StringBuilder reverse()

功能:将当前对象中的字符序列反转。

示例:

public class ReverseExample { public static void main(String[] args) { // StringBuffer reverse StringBuffer stringBuffer = new StringBuffer(\"Hello\"); stringBuffer.reverse(); System.out.println(\"StringBuffer reverse 结果: \" + stringBuffer.toString()); // StringBuilder reverse StringBuilder stringBuilder = new StringBuilder(\"Hello\"); stringBuilder.reverse(); System.out.println(\"StringBuilder reverse 结果: \" + stringBuilder.toString()); }}

运行结果:


6. 设置方法

setCharAt(int index, char ch)

语法void setCharAt(int index, char ch)

功能:将指定索引位置的字符替换为指定的字符 ch。

示例

public class SetCharAtExample { public static void main(String[] args) { // StringBuffer setCharAt StringBuffer stringBuffer = new StringBuffer(\"Hello\"); stringBuffer.setCharAt(1, \'a\'); System.out.println(\"StringBuffer setCharAt 结果: \" + stringBuffer.toString()); // StringBuilder setCharAt StringBuilder stringBuilder = new StringBuilder(\"Hello\"); stringBuilder.setCharAt(1, \'a\'); System.out.println(\"StringBuilder setCharAt 结果: \" + stringBuilder.toString()); }}

运行结果:


setLength(int newLength)

语法:void setLength(int newLength)

功能:设置当前对象的长度。如果 newLength 小于当前长度,则会截断字符序列;如果 newLength 大于当前长度,则会在末尾填充空字符(\'\\0\')。

示例:

public class SetLengthExample { public static void main(String[] args) { // StringBuffer setLength StringBuffer stringBuffer = new StringBuffer(\"Hello\"); stringBuffer.setLength(3); System.out.println(\"StringBuffer setLength 截断结果: \" + stringBuffer.toString()); stringBuffer.setLength(5); System.out.println(\"StringBuffer setLength 扩充结果: \" + stringBuffer.toString()); // StringBuilder setLength StringBuilder stringBuilder = new StringBuilder(\"Hello\"); stringBuilder.setLength(3); System.out.println(\"StringBuilder setLength 截断结果: \" + stringBuilder.toString()); stringBuilder.setLength(5); System.out.println(\"StringBuilder setLength 扩充结果: \" + stringBuilder.toString()); }}

运行结果:


7. 获取字符方法

charAt(int index)

语法char charAt(int index)

功能:返回指定索引位置的字符。

示例:

public class CharAtExample { public static void main(String[] args) { // StringBuffer charAt StringBuffer stringBuffer = new StringBuffer(\"Hello\"); char c1 = stringBuffer.charAt(1); System.out.println(\"StringBuffer charAt 结果: \" + c1); // StringBuilder charAt StringBuilder stringBuilder = new StringBuilder(\"Hello\"); char c2 = stringBuilder.charAt(1); System.out.println(\"StringBuilder charAt 结果: \" + c2); }}

运行结果:


8. 查找方法

indexOf(String str)

语法int indexOf(String str)

功能:返回指定字符串 str 在当前对象中第一次出现的索引位置。如果未找到,则返回 -1。

示例:

public class IndexOfExample { public static void main(String[] args) { // StringBuffer indexOf StringBuffer stringBuffer = new StringBuffer(\"HelloWorld\"); int index1 = stringBuffer.indexOf(\"World\"); System.out.println(\"StringBuffer indexOf 结果: \" + index1); // StringBuilder indexOf StringBuilder stringBuilder = new StringBuilder(\"HelloWorld\"); int index2 = stringBuilder.indexOf(\"World\"); System.out.println(\"StringBuilder indexOf 结果: \" + index2); }}

运行结果:


indexOf(String str, int fromIndex)

语法:int indexOf(String str, int fromIndex)

功能:指定的索引 fromIndex 开始,返回指定字符串 str 在当前对象中第一次出现的索引位置。如果未找到,则返回 -1。

示例:

public class IndexOfWithStartExample { public static void main(String[] args) { // StringBuffer indexOf 带起始位置 StringBuffer stringBuffer = new StringBuffer(\"HelloWorldHello\"); int index1 = stringBuffer.indexOf(\"Hello\", 6); System.out.println(\"StringBuffer indexOf 带起始位置结果: \" + index1); // StringBuilder indexOf 带起始位置 StringBuilder stringBuilder = new StringBuilder(\"HelloWorldHello\"); int index2 = stringBuilder.indexOf(\"Hello\", 6); System.out.println(\"StringBuilder indexOf 带起始位置结果: \" + index2); }}

运行结果:


lastIndexOf(String str)

语法:int lastIndexOf(String str)

功能:返回指定字符串 str 在当前对象中最后一次出现的索引位置。如果未找到,则返回 -1。

示例:

public class LastIndexOfExample { public static void main(String[] args) { // StringBuffer lastIndexOf StringBuffer stringBuffer = new StringBuffer(\"HelloWorldHello\"); int index1 = stringBuffer.lastIndexOf(\"Hello\"); System.out.println(\"StringBuffer lastIndexOf 结果: \" + index1); // StringBuilder lastIndexOf StringBuilder stringBuilder = new StringBuilder(\"HelloWorldHello\"); int index2 = stringBuilder.lastIndexOf(\"Hello\"); System.out.println(\"StringBuilder lastIndexOf 结果: \" + index2); }}

运行结果:


lastIndexOf(String str, int fromIndex)

语法:int lastIndexOf(String str, int fromIndex)

功能:从指定的索引 fromIndex 开始向前搜索,返回指定字符串 str 在当前对象中最后一次出现的索引位置。如果未找到,则返回 -1。

示例:

public class LastIndexOfWithStartExample { public static void main(String[] args) { // StringBuffer lastIndexOf 带起始位置 StringBuffer stringBuffer = new StringBuffer(\"HelloWorldHello\"); int index1 = stringBuffer.lastIndexOf(\"Hello\", 9); System.out.println(\"StringBuffer lastIndexOf 带起始位置结果: \" + index1); // StringBuilder lastIndexOf 带起始位置 StringBuilder stringBuilder = new StringBuilder(\"HelloWorldHello\"); int index2 = stringBuilder.lastIndexOf(\"Hello\", 9); System.out.println(\"StringBuilder lastIndexOf 带起始位置结果: \" + index2); }}

运行结果:


9. 长度和子字符串方法

length()

语法:int length()

功能:返回当前对象中字符序列的长度。

示例:

public class LengthExample { public static void main(String[] args) { // StringBuffer length StringBuffer stringBuffer = new StringBuffer(\"Hello\"); int len1 = stringBuffer.length(); System.out.println(\"StringBuffer length 结果: \" + len1); // StringBuilder length StringBuilder stringBuilder = new StringBuilder(\"Hello\"); int len2 = stringBuilder.length(); System.out.println(\"StringBuilder length 结果: \" + len2); }}

运行结果:


substring(int start)

语法:String substring(int start)

功能:返回从指定索引 start 开始到字符序列末尾的子字符串。

示例:

public class SubstringSingleParamExample { public static void main(String[] args) { // StringBuffer substring 单参数 StringBuffer stringBuffer = new StringBuffer(\"HelloWorld\"); String sub1 = stringBuffer.substring(5); System.out.println(\"StringBuffer substring 单参数结果: \" + sub1); // StringBuilder substring 单参数 StringBuilder stringBuilder = new StringBuilder(\"HelloWorld\"); String sub2 = stringBuilder.substring(5); System.out.println(\"StringBuilder substring 单参数结果: \" + sub2); }}

运行结果:


substring(int start, int end)

语法String substring(int start, int end)

功能:返回从索引 start(包含)到索引 end(不包含)之间的子字符串。

示例:

public class SubstringTwoParamsExample { public static void main(String[] args) { // StringBuffer substring 双参数 StringBuffer stringBuffer = new StringBuffer(\"HelloWorld\"); String sub1 = stringBuffer.substring(0, 5); System.out.println(\"StringBuffer substring 双参数结果: \" + sub1); // StringBuilder substring 双参数 StringBuilder stringBuilder = new StringBuilder(\"HelloWorld\"); String sub2 = stringBuilder.substring(0, 5); System.out.println(\"StringBuilder substring 双参数结果: \" + sub2); }}

运行结果:


10. 转换为字符串方法

toString()

语法:String toString()

功能:将当前对象中的字符序列转换为一个不可变的 String 对象。

示例:

public class ToStringExample { public static void main(String[] args) { // StringBuffer toString StringBuffer stringBuffer = new StringBuffer(\"Hello\"); String str1 = stringBuffer.toString(); System.out.println(\"StringBuffer toString 结果: \" + str1); // StringBuilder toString StringBuilder stringBuilder = new StringBuilder(\"Hello\"); String str2 = stringBuilder.toString(); System.out.println(\"StringBuilder toString 结果: \" + str2); }}

运行结果:

四、StringStringBuilder 和 StringBuffer 的效率对比和优化策略的详细解析

1. 问题根源:String 的不可变性

String 是不可变类,每次拼接(如 s += i)都会触发以下操作:

①创建一个新的 StringBuilder 对象(隐式创建)。

②调用 append() 方法将当前 si 拼接。

③调用 toString() 生成新的 String 对象。

④旧的 String 对象成为垃圾,等待 GC 回收。

示例代码分析:

String s = \"\";for (int i = 0; i < 100000; i++) { s += i; // 等价于 s = new StringBuilder().append(s).append(i).toString();}

问题:循环 10 万次会创建 10 万个 StringBuilder 和 String 对象,导致:

       频繁内存分配和对象复制。

       GC 压力剧增(大量短期对象进入年轻代,触发频繁 Minor GC)。

结果耗时 3827 毫秒,效率极低。

2. StringBuilder 的高效性

StringBuilder 是可变字符序列,底层基于动态扩容的 byte[ ](或 char[ ]),直接修改原数组,避免频繁对象创建。

示例代码分析:

StringBuilder s = new StringBuilder();for (int i = 0; i < 100000; i++) { s.append(i); // 直接操作底层数组,无需创建新对象}

优势:

单次对象创建:只初始化一个 StringBuilder 对象

动态扩容优化:底层数组按需扩容(扩容策略为 原容量*2 + 2)。

无额外 GC 压力:对象复用,减少垃圾产生。

结果:耗时 7 毫秒,效率提升近 400 倍。

3. StringBuffer 的线程安全代价

StringBufferStringBuilder 功能相同,但所有方法用 synchronized 修饰以保证线程安全。 效率对比:

单线程环境:StringBuffer 因同步锁开销,效率低于 StringBuilder(测试中可能慢 10%~20%)。

多线程环境:需共享变量时,StringBuffer 是唯一选择。

4. 性能优化策略

(1) 预分配容量

默认容量:StringBuilder 初始容量为 16。

扩容成本:每次扩容需复制旧数组到新数组,时间复杂度为 O(n)

优化方案:预估最终字符串长度,直接指定初始容量。

StringBuilder sb = new StringBuilder(100000); // 直接分配足够空间,避免扩容

5. 效率对比总结

类名 线程安全 单线程效率 适用场景 String 不可变 极低 少量字符串操作或常量定义 StringBuilder 不安全 最高 单线程频繁字符串操作 StringBuffer 安全 中等 多线程共享变量的字符串操作

6. 实际测试数据

操作类型 循环次数 耗时(毫秒) String 拼接 100,000 3827 StringBuilder 100,000 7 StringBuffer 100,000 12~15

7. 总结与建议

  • 优先选择 StringBuilder:在单线程环境下,始终优先使用 StringBuilder

  • 避免 String 拼接循环:尤其在数据量大时,直接使用 StringBuilder

  • 预分配容量:预估字符串长度,减少扩容次数。

  • 多线程场景用 StringBuffer:仅在需要线程安全时使用,否则性能损耗明显。

理解这些差异和优化策略,可以显著提升字符串操作的性能,减少不必要的资源消耗。