Java StringBuffer 与 StringBuilder
String 的内容不可变,每次用 + 拼接都会新建对象,循环里大量拼接既慢又费内存。为此 Java 提供了可变的 StringBuffer 与 StringBuilder,能直接在原对象上增删改。
为什么需要可变字符串
用 + 拼接字符串时,每拼接一次就产生一个新的 String,循环次数多了开销很大:
public class SlowConcat {
public static void main(String[] args) {
String s = "";
for (int i = 0; i < 10000; i++) {
s = s + i; // 每轮都新建一个 String 对象,旧对象等待回收
}
System.out.println(s.length()); // 输出:38890
}
}
String、StringBuffer、StringBuilder 的区别
| 类型 | 内容可否修改 | 线程安全 | 速度 | 适用场景 |
|---|---|---|---|---|
| String | 不可变 | 安全 | 慢(拼接时) | 内容固定、少量拼接 |
| StringBuffer | 可变 | 安全(方法加锁) | 较快 | 多线程环境拼接 |
| StringBuilder | 可变 | 不安全 | 最快 | 单线程拼接(首选) |
单线程拼接优先用 StringBuilder;StringBuffer 与它 API 完全一致,只是方法加了 synchronized,多线程共享时才需要。
StringBuilder 基本用法
// 片段需放进 main 方法中运行
StringBuilder sb = new StringBuilder("Hello");
sb.append(" Java"); // 末尾追加
sb.insert(5, ","); // 在下标 5 处插入
sb.delete(5, 6); // 删除 [5, 6) 区间,即刚插入的逗号
sb.replace(0, 5, "Hi"); // 用 "Hi" 替换 [0, 5)
System.out.println(sb); // 输出:Hi Java
常用方法一览
| 方法 | 作用 |
|---|---|
| append(x) | 末尾追加任意类型 |
| insert(i, x) | 在指定下标插入 |
| delete(a, b) | 删除 [a, b) 区间 |
| deleteCharAt(i) | 删除单个字符 |
| replace(a, b, s) | 区间替换 |
| reverse() | 内容反转 |
| setLength(n) | 截断或扩容到长度 n |
| charAt(i) / length() | 取值 / 取长度 |
| toString() | 转成不可变的 String |
完整可运行示例
把上面的方法串起来,直接编译运行看效果:
public class StringBuilderDemo {
public static void main(String[] args) {
StringBuilder sb = new StringBuilder("Hello");
sb.append(" Java"); // 追加
System.out.println(sb); // 输出:Hello Java
sb.insert(5, ","); // 插入逗号
System.out.println(sb); // 输出:Hello, Java
sb.delete(5, 6); // 删掉逗号
System.out.println(sb); // 输出:Hello Java
sb.setLength(5); // 只保留前 5 个字符
System.out.println(sb); // 输出:Hello
sb.reverse(); // 反转
System.out.println(sb); // 输出:olleH
String result = sb.toString().toUpperCase();
System.out.println(result); // 输出:OLLEH
}
}
拼接性能对比
同样的拼接任务,StringBuilder 明显更快:
public class SpeedCompare {
public static void main(String[] args) {
long start = System.currentTimeMillis();
StringBuilder sb = new StringBuilder();
for (int i = 0; i < 100000; i++) {
sb.append("a"); // 原地修改,不产生中间对象
}
long cost = System.currentTimeMillis() - start;
System.out.println("StringBuilder 耗时:" + cost + " 毫秒");
}
}
小结:要反复拼接、删除、插入字符串时,忘掉 +,改用 StringBuilder(单线程首选);多线程共享可变字符串时再换成线程安全的 StringBuffer。两者方法几乎相同,可互相替换。