杰瑞科技汇

StringBuffer与StringBuilder有何区别?

什么是 StringBuffer

StringBuffer 是 Java 中一个用于创建可变字符串的类,与 Java 中不可变的 String 类不同,StringBuffer 对象的内容可以被修改,比如可以在字符串的末尾追加、插入或删除字符。

StringBuffer与StringBuilder有何区别?-图1
(图片来源网络,侵删)

你可以把它想象成一个“字符序列”的容器,你可以随时向这个容器里添加或移除字符,而这个容器本身(对象引用)保持不变。

StringBuffer 的核心特性

  1. 可变性:这是 StringBuffer 最核心的特性,一旦创建了 StringBuffer 对象,你就可以修改它所包含的字符序列,而无需像 String 那样创建新的对象。
  2. 线程安全StringBuffer 的所有公共方法(如 append(), insert(), delete() 等)都被 synchronized 关键字修饰,这意味着在任何时刻,只有一个线程可以执行这些方法。StringBuffer线程安全的,可以在多线程环境中安全使用。
  3. 性能:由于线程安全需要额外的同步开销,StringBuffer 的性能通常比它的非线程安全版本 StringBuilder 稍慢,在单线程环境下,推荐使用 StringBuilder 以获得更好的性能。
  4. 字符序列:它内部维护一个字符数组,当容量不足以容纳新添加的字符时,会自动扩容(通常是创建一个更大的新数组,并复制旧数据)。

StringBufferString 的关键区别

特性 String StringBuffer
可变性 不可变 (Immutable) 可变 (Mutable)
线程安全 不安全 (非同步) 安全 (同步)
性能 频繁修改时性能差,因为每次修改都创建新对象 频繁修改时性能好,直接修改内部数组
内存 修改时会产生新的内存对象 修改时通常不产生新对象(扩容除外)
使用场景 存储不变的文本,如常量、配置信息 需要频繁进行字符串拼接、修改的场景

一个简单的例子来展示区别:

// String 示例
String s1 = "Hello";
String s2 = s1 + " World"; 
// 这里 s1 没有变,而是创建了一个新的 String 对象 "Hello World" 赋值给 s2
// s1 仍然指向 "Hello"
// StringBuffer 示例
StringBuffer sb = new StringBuffer("Hello");
sb.append(" World"); 
// 这里直接在 sb 对象的内部进行了修改
// sb 现在的内容就是 "Hello World"

StringBuffer 的常用方法

StringBuffer 提供了丰富的方法来操作字符串。

1 构造方法

// 1. 创建一个空的 StringBuffer,初始容量为 16
StringBuffer sb1 = new StringBuffer();
// 2. 创建一个指定容量的空的 StringBuffer
StringBuffer sb2 = new StringBuffer(32);
// 3. 创建一个包含指定字符串的 StringBuffer,初始容量为 16 + 字符串长度
StringBuffer sb3 = new StringBuffer("Hello");

2 追加和插入

append()insert() 方法返回的是 StringBuffer 对象本身,这被称为“方法链”或“流式调用”。

StringBuffer与StringBuilder有何区别?-图2
(图片来源网络,侵删)
StringBuffer sb = new StringBuffer("Start");
// append(): 在末尾追加
sb.append(" Java"); // sb 变为 "Start Java"
System.out.println(sb);
// insert(): 在指定位置插入
sb.insert(5, "Learning "); // 在索引 5 的位置插入 "Learning "
System.out.println(sb); // 输出 "Start Learning Java"

3 删除和替换

StringBuffer sb = new StringBuffer("Hello, Programming, World!");
// delete(int start, int end): 删除从 start 到 end-1 的字符
sb.delete(7, 19); // 删除 " Programming"
System.out.println(sb); // 输出 "Hello, World!"
// deleteCharAt(int index): 删除指定索引的字符
sb.deleteCharAt(5); // 删除索引 5 的字符 ','
System.out.println(sb); // 输出 "Hello World!"
// replace(int start, int end, String str): 替换从 start 到 end-1 的字符
sb.replace(6, 11, "Java"); // 将 "World" 替换为 "Java"
System.out.println(sb); // 输出 "Hello Java"

4 反转和截取

StringBuffer sb = new StringBuffer("ABCDEF");
// reverse(): 反转字符串
sb.reverse();
System.out.println(sb); // 输出 "FEDCBA"
// substring(int start): 从指定位置开始截取到末尾
String sub1 = sb.substring(2);
System.out.println(sub1); // 输出 "DCBA"
// substring(int start, int end): 截取从 start 到 end-1 的子串
String sub2 = sb.substring(1, 4);
System.out.println(sub2); // 输出 "EDC"

5 其他实用方法

StringBuffer sb = new StringBuffer("Java is great");
// capacity(): 获取当前容量(内部数组大小)
System.out.println("Capacity: " + sb.capacity()); // 输出 "Capacity: 27" (16 + "Java is great".length())
// length(): 获取当前字符串长度
System.out.println("Length: " + sb.length()); // 输出 "Length: 13"
// charAt(int index): 获取指定索引的字符
char c = sb.charAt(0);
System.out.println("First char: " + c); // 输出 "First char: J"
// setCharAt(int index, char ch): 设置指定索引的字符
sb.setCharAt(0, 'j');
System.out.println(sb); // 输出 "java is great"
// toString(): 转换为不可变的 String 对象
String finalString = sb.toString();
System.out.println("Final String: " + finalString);
System.out.println("Is finalString a String? " + (finalString instanceof String)); // true

StringBuffer vs. StringBuilder

这是面试中非常经典的问题。

特性 StringBuffer StringBuilder
线程安全 (方法被 synchronized 修饰)
性能 稍慢 (因为有同步开销) 更快 (无同步开销)
出现版本 Java 1.0 Java 5
使用建议 多线程环境 单线程环境 (绝大多数情况)

如何选择?

  • 默认使用 StringBuilder:在现代 Java 开发中,绝大多数字符串操作都在单线程中进行。StringBuilder 因为其更高的性能,是首选。
  • 仅在需要线程安全时使用 StringBuffer:如果你的字符串操作会被多个线程同时访问和修改,并且你需要保证数据的一致性,那么必须使用 StringBuffer,这种情况相对较少。

性能对比示例

下面是一个简单的性能测试,展示 StringBuilder 在循环拼接时的巨大优势。

public class PerformanceTest {
    public static void main(String[] args) {
        int iterations = 100000;
        // 测试 String
        long startTime = System.currentTimeMillis();
        String str = "";
        for (int i = 0; i < iterations; i++) {
            str += i; // 每次循环都创建一个新的 String 对象
        }
        long endTime = System.currentTimeMillis();
        System.out.println("String拼接耗时: " + (endTime - startTime) + " ms");
        // 测试 StringBuffer
        startTime = System.currentTimeMillis();
        StringBuffer sb = new StringBuffer();
        for (int i = 0; i < iterations; i++) {
            sb.append(i); // 在同一个对象上操作
        }
        endTime = System.currentTimeMillis();
        System.out.println("StringBuffer拼接耗时: " + (endTime - startTime) + " ms");
        // 测试 StringBuilder
        startTime = System.currentTimeMillis();
        StringBuilder sBuilder = new StringBuilder();
        for (int i = 0; i < iterations; i++) {
            sBuilder.append(i); // 在同一个对象上操作,无同步开销
        }
        endTime = System.currentTimeMillis();
        System.out.println("StringBuilder拼接耗时: " + (endTime - startTime) + " ms");
    }
}

运行结果(可能因机器不同而异):

StringBuffer与StringBuilder有何区别?-图3
(图片来源网络,侵删)
String拼接耗时: 4562 ms
StringBuffer拼接耗时: 15 ms
StringBuilder拼接耗时: 8 ms

从结果可以清晰地看到,String 的拼接方式在循环中性能极差,而 StringBuilder 是最快的,StringBuffer 略慢于 StringBuilder

  • StringBuffer 是 Java 提供的一个可变、线程安全的字符串操作类。
  • 它的核心价值在于可以高效地修改字符串内容,避免了 String 类因不可变性带来的性能问题。
  • 由于其同步特性,StringBuffer 的性能开销比 StringBuilder 大。
  • 在日常的单线程编程中,优先使用 StringBuilder,只有在明确需要处理多线程并发修改字符串的场景下,才考虑使用 StringBuffer
分享:
扫描分享到社交APP
上一篇
下一篇