核心概念
特性 | String | StringBuilder |
---|---|---|
可变性 | 不可变(Immutable) | 可变(Mutable) |
线程安全 | 线程安全(天然) | 非线程安全(单线程优化) |
内存效率 | 频繁修改时内存开销大 | 修改时内存开销小 |
适用场景 | 常量字符串、少量拼接 | 频繁修改字符串(循环/复杂逻辑) |
性能 | 修改操作慢(O(n)) | 修改操作快(O(1) 均摊) |
操作步骤详解
1. 创建对象
// String
String s1 = "Hello"; // 字面量(入字符串常量池)
String s2 = new String("World"); // 堆内存新对象
// StringBuilder
StringBuilder sb = new StringBuilder(); // 默认容量16
StringBuilder sb = new StringBuilder(100); // 指定初始容量
StringBuilder sb = new StringBuilder("Init"); // 初始值+容量(16+4)
2. 追加内容
// String(每次生成新对象)
s1 = s1 + " Java"; // 底层编译为StringBuilder,但循环中低效
// StringBuilder(原地修改)
sb.append(" Java")
.append(2023) // 支持链式调用
.append('!');
3. 插入/删除
// 在索引5插入
sb.insert(5, " Awesome");
// 删除[5,13)的字符
sb.delete(5, 13);
// 删除单个字符
sb.deleteCharAt(4);
4. 替换/反转
// 替换[0,4)为"Hi"
sb.replace(0, 4, "Hi");
// 反转字符串
sb.reverse();
5. 转换与获取
String result = sb.toString(); // 最终转换为String
int len = sb.length(); // 当前字符数
int cap = sb.capacity(); // 当前容量
常见错误与避坑指南
循环中使用String拼接
// 错误!每次循环创建新对象 String s = ""; for (int i = 0; i < 10000; i++) { s += i; // 性能灾难! } // 正确:使用StringBuilder StringBuilder sb = new StringBuilder(); for (int i = 0; i < 10000; i++) { sb.append(i); }
忽略线程安全
// StringBuilder非线程安全! // 多线程环境需改用StringBuffer StringBuffer safeBuffer = new StringBuffer();
未初始化直接使用
StringBuilder sb; // 未初始化 sb.append("data"); // NullPointerException!
关键注意事项
容量 vs 长度
capacity()
:当前可存储的字符总数(自动扩容)length()
:实际存储的字符数- 扩容规则:
newCapacity = (oldCapacity * 2) + 2
内存优化
- 预估大小:
new StringBuilder(500)
避免多次扩容 - 清空内容:
sb.setLength(0)
(复用对象,比new
更高效)
- 预估大小:
API选择
- 单次操作:
String.format()
或"a" + "b"
- 复杂操作:优先
StringBuilder
- 单次操作:
性能优化技巧
预分配容量(避免扩容拷贝)
// 预估最终长度约200 StringBuilder sb = new StringBuilder(200);
链式调用优化
// 链式调用避免临时变量 sb.append("Name: ").append(name).append(", Age: ").append(age);
复用StringBuilder
StringBuilder sb = new StringBuilder(); for (Task task : tasks) { sb.setLength(0); // 清空内容复用 sb.append(task.process()); // 使用结果... }
最佳实践总结
场景 | 推荐选择 | 理由 |
---|---|---|
静态字符串、配置信息 | String | 利用常量池、线程安全 |
循环内字符串拼接 | StringBuilder | 避免内存浪费 |
高并发环境 | StringBuffer | 线程安全 |
已知最终长度的大字符串构建 | StringBuilder+预分配 | 减少扩容开销 |
简单格式化(<5次拼接) | String + + |
代码简洁,编译器自动优化 |
性能对比实测
// 测试代码片段
long start = System.currentTimeMillis();
String s = "";
for (int i = 0; i < 100000; i++) {
s += i; // String拼接
}
System.out.println("String time: " + (System.currentTimeMillis()-start));
start = System.currentTimeMillis();
StringBuilder sb = new StringBuilder();
for (int i = 0; i < 100000; i++) {
sb.append(i); // StringBuilder
}
System.out.println("StringBuilder time: " + (System.currentTimeMillis()-start));
典型结果(JDK 17, i7-11800H):
String time: 5832 ms
StringBuilder time: 8 ms
结论:频繁修改字符串时,StringBuilder性能可提升数百倍!
终极选择流程图
开始
│
├─ 字符串是否需要修改? ──否─→ 使用String
│
是
│
├─ 多线程环境? ──是─→ 使用StringBuffer
│
否
│
└─ 使用StringBuilder(预分配容量更佳)