Skip to content

String与StringBuilder的区别

在 Java 中,StringStringBuilder (以及它的线程安全版本 StringBuffer) 是处理字符串的核心类,它们的关键区别在于 可变性 及其带来的性能影响。以下是主要区别:

  1. 不可变性 vs 可变性

    • String (不可变): String 对象一旦创建,其内容就不能被修改。任何看似修改 String 的操作(如 concat(), +, replace(), substring(), toUpperCase() 等),实际上都会创建一个全新的 String 对象在内存中。原始字符串保持不变。
    • StringBuilder (可变): StringBuilder 对象代表一个可变的字符序列。你可以通过其方法(如 append(), insert(), delete(), replace(), reverse() 等)直接修改其内部字符数组的内容,而不会创建新的对象(除非内部数组需要扩容)。
  2. 性能 (尤其是在循环或频繁修改时)

    • String 由于不可变性,在循环或需要大量拼接/修改字符串的场景下(例如在一个循环中反复使用 +=concat()),会产生大量临时的、中间态的 String 对象。创建和销毁这些对象会带来显著的内存分配和垃圾回收开销,导致性能低下
    • StringBuilder 由于直接在现有缓冲区上修改,避免了创建大量临时对象。它在进行大量字符串连接或修改操作时性能显著优于 String。这是它存在的主要原因。
  3. 内存使用

    • String 频繁修改会导致内存中存在许多不再使用的中间字符串对象,等待垃圾回收。这会占用更多内存,并可能增加 GC 停顿时间。
    • StringBuilder 通常更节省内存,尤其是在大量修改操作中,因为它主要操作一个可扩展的缓冲区。虽然内部数组在需要时会扩容(创建新数组并复制数据),但这比创建大量完整的新字符串对象开销小得多。
  4. 线程安全性

    • String 由于其不可变性,String 对象本质上是线程安全的。多个线程可以安全地读取同一个 String 对象。
    • StringBuilder 不是线程安全的。它的方法没有同步机制。如果多个线程需要同时修改同一个 StringBuilder 实例,可能会导致数据不一致。如果需要线程安全,应使用 StringBuffer(其方法使用 synchronized 关键字修饰)。
  5. API 和初始化

    • String 通常通过双引号 "Hello" 字面量创建(存储在字符串常量池),或通过 new String(...) 创建(在堆上)。提供丰富的字符串操作功能,但都返回新字符串。
    • StringBuilder 必须通过 new StringBuilder()new StringBuilder(int capacity)new StringBuilder(String str) 创建。提供专注于修改缓冲区内容的方法(append, insert, delete, replace, setLength, reverse 等)。
  6. equals()hashCode() 行为

    • String 重写了 equals()hashCode() 方法,基于字符串的内容进行比较。
    • StringBuilder 没有重写 Objectequals()hashCode() 方法。比较两个 StringBuilder 对象是基于对象引用(内存地址),而不是内容。要比较内容,需要先调用 toString() 转换为 String 再比较。

总结表格

特性StringStringBuilder
可变性不可变可变
修改操作创建新对象修改现有对象
性能 (修改) (大量对象创建/GC) (原地修改)
内存开销 (大量中间对象) (一个缓冲区)
线程安全 (因不可变) (用 StringBuffer 替代)
初始化字面量 或 new String(...)new StringBuilder(...)
equals/hash基于内容基于对象引用
主要用途表示固定不变的文本高效构建或修改字符串 (拼接、替换等)

何时使用哪个?

  1. 使用 String

    • 当字符串的值在创建后不会改变时(例如,常量、配置信息、键值)。
    • 需要依赖内容比较 (equals) 或哈希 (hashCode) 时。
    • 需要线程安全地共享字符串时(读取是安全的)。
    • 简单的、少量的字符串连接(编译器有时会优化 +StringBuilder,但不要在循环中依赖此优化!)。
  2. 使用 StringBuilder (或 StringBuffer):

    • 当需要在循环频繁连接字符串时(绝对首选)。
    • 当需要多次修改一个字符串的内容(插入、删除、替换等)时。
    • 性能内存效率是关键考虑因素,且修改操作很多时。
    • StringBuffer vs StringBuilder: 需要线程安全时用 StringBuffer;单线程环境下追求最高性能用 StringBuilder(更常用)。

黄金法则:

  • 永远不要在循环中使用 String+concat 进行大量拼接!始终使用 StringBuilder (或 StringBuffer)
  • 对于简单的、单次的字符串连接,String+ 是可读且可接受的(编译器通常会优化)。
  • 优先考虑代码可读性和正确性,在性能确实成为瓶颈时才进行优化。但在循环拼接这个特定场景下,从一开始就用 StringBuilder 就是最佳实践。