Loading

[Java基础]StringBuilder 和 StringBuffer 和 String 比较

String、StringBuffer、StringBuilder的区别

  • 可变与不可变:String是不可变字符串对象,StringBuilder和StringBuffer是可变字符串对象(其内部的字符数组长度可变)。
  • 是否多线程安全:String中的对象是不可变的,也就可以理解为常量,显然线程安全。StringBuffer 与 StringBuilder 中的方法和功能完全是等价的,只是StringBuffer 中的方法大都采用了synchronized 关键字进行修饰,因此是线程安全的,而 StringBuilder 没有这个修饰,可以被认为是非线程安全的。
  • String、StringBuilder、StringBuffer三者的执行效率:StringBuilder > StringBuffer > String 当然这个是相对的,不一定在所有情况下都是这样。比如String str = "hello"+ "world"的效率就比 StringBuilder st = new StringBuilder().append("hello").append("world")要高。因此,这三个类是各有利弊,应当根据不同的情况来进行选择

使用:

  • 当字符串相加操作或者改动较少的情况下,建议使用 String str="hello"这种形式;
  • 当字符串相加操作较多的情况下,建议使用StringBuilder,如果采用了多线程,则使用StringBuffer。

可变性

  • String 是不可变的。

  • StringBuilder 与 StringBuffer 都继承自 AbstractStringBuilder 类,在 AbstractStringBuilder 中也是使用字符数组保存字符串,不过没有使用 final 和 private 关键字修饰,最关键的是这个 AbstractStringBuilder 类还提供了很多修改字符串的方法。比如 append 方法。从如下代码中可以看到,append方法并不是创建一个新的string对象,而是在原有的string中的char数组中直接进行修改(当然如果数组长度不够了会进行扩容产生一个新的数组)。

abstract class AbstractStringBuilder implements Appendable, CharSequence {
    char[] value;
    public AbstractStringBuilder append(String str) {
        if (str == null)
            return appendNull();
        int len = str.length();
        ensureCapacityInternal(count + len);
        str.getChars(0, len, value, count);
        count += len;
        return this;
    }
    //...
}

线程安全性

  • String 中的对象是不可变的,也就可以理解为常量,线程安全。

AbstractStringBuilder 是 StringBuilder 与 StringBuffer 的公共父类,定义了一些字符串的基本操作,如 expandCapacity、append、insert、indexOf 等公共方法。

  • StringBuilder 并没有对方法进行加同步锁,所以是非线程安全的。

  • StringBuffer 对方法加了同步锁或者对调用的方法加了同步锁,所以是线程安全的。

性能

  • 每次对 String 类型进行改变的时候,都会生成一个新的 String 对象,然后将指针指向新的 String 对象。
  • StringBuffer 每次都会对 StringBuffer 对象本身进行操作,而不是生成新的对象并改变对象引用。
  • 相同情况下使用 StringBuilder 相比使用 StringBuffer 仅能获得 10%~15% 左右的性能提升,但却要冒多线程不安全的风险。

对于三者使用的总结:

  • 操作少量的数据: 适用 String
  • 单线程操作字符串缓冲区下操作大量数据: 适用 StringBuilder
  • 多线程操作字符串缓冲区下操作大量数据: 适用 StringBuffer

在Java中,StringStringBuilderStringBuffer 是三个用于处理字符串的类,但它们在特性和用法上有所不同。以下是每个类的详细解释以及它们的区别:

1. String

  • 不可变性:String 对象是不可变的。一旦创建了一个 String 对象,其值就不能改变。如果需要修改字符串,则会创建一个新的 String 对象。
  • 线程安全:由于 String 对象是不可变的,因此是线程安全的。
  • 性能:由于每次修改都会创建新的对象,频繁的字符串操作可能会导致性能问题和内存开销的增加。
String str = "Hello";
str += " World"; // 新建了一个字符串对象,str引用了新的"Hello World"对象

2. StringBuilder

  • 可变性:StringBuilder 是可变的,意味着它的内容可以被修改。
  • 线程不安全:因为它不是同步的,因此 StringBuilder 是线程不安全的,适用于单线程环境。
  • 性能:由于不需要创建新的对象来进行修改,StringBuilder 在频繁操作字符串时性能表现更好。
StringBuilder sb = new StringBuilder("Hello");
sb.append(" World"); // 修改了同一个StringBuilder对象

3. StringBuffer

  • 可变性:StringBuffer 也是可变的,可以修改其内容。
  • 线程安全:StringBuffer 是线程安全的,因为它的方法是同步的,使用了synchronied关键字,适用于多线程环境。
  • 性能:由于使用了同步,即使在单线程环境中,StringBuffer 的性能可能会比 StringBuilder 略差。
StringBuffer sbf = new StringBuffer("Hello");
sbf.append(" World"); // 修改了同一个StringBuffer对象

总结区别

  • 不可变性: String 是不可变的,StringBuilderStringBuffer 是可变的。
  • 线程安全: String 是线程安全的,因为它是不可变的;StringBuffer 是线程安全的,因为它的方法是同步的;StringBuilder 是线程不安全的,它的设计目的是用在单线程环境中。
  • 性能: StringBuilder 通常比 StringBuffer 性能更好,适用于单线程环境;StringBuffer 适用于需要线程安全的多线程环境;而 String 由于不可变性,频繁操作字符串时性能较差。

选择哪个类要根据具体的应用场景和需求来决定。如果在单线程环境中进行大量字符串操作,StringBuilder 是首选;如果在多线程环境中操作字符串,StringBuffer 是优选;而对于完全不可变的字符串,使用 String 是最佳选择。

为什么StringBuilderStringBuffer可变

在 Java 中,StringBuilderStringBuffer 是可变的字符序列,主要用于在字符串频繁修改(如追加、插入、删除等)时提高性能。它们与 String 类的最大区别在于:String 是不可变的,而 StringBuilderStringBuffer 是可变的。这种可变性通过底层的动态数组和方法操作实现,以下是它们如何实现可变性的详细解释。

1. 底层使用可变的字符数组

StringBuilderStringBuffer 的核心是一个 可变的字符数组(char[])。这个数组用于存储当前的字符串内容,当字符串内容改变时,字符数组的内容会被直接修改,而不需要像 String 那样每次修改都创建新的对象。

初始容量和扩展

  • 当创建 StringBuilderStringBuffer 时,它们会分配一个初始容量的字符数组(通常默认为 16 个字符的容量)。这个初始容量是可以指定的,如果没有指定,默认的容量是 16。

  • 当字符数组的容量不足以容纳新的字符时,StringBuilderStringBuffer 会动态扩展数组的大小。扩展的过程是通过 创建一个新的、更大的字符数组,然后将旧数组中的内容复制到新数组中。

例子:

StringBuilder sb = new StringBuilder();
sb.append("Hello");
sb.append(" World");
System.out.println(sb.toString());
  • 原理:在上面的代码中,StringBuilder 的内部字符数组被初始化为默认容量 16。当 append() 方法被调用时,字符串 "Hello" 和 "World" 被直接追加到内部的字符数组中,数组的大小自动扩展以适应更多的字符。

2. 可变性实现机制:修改内部数组

StringBuilderStringBufferappend()insert()delete() 等方法直接操作内部的字符数组,而不是像 String 那样返回一个新的对象。这使得这些操作能够在原地修改字符串内容,避免了频繁创建新对象的开销。

例子:append() 操作

StringBuilder sb = new StringBuilder("Hello");
sb.append(" World"); // 修改了内部的字符数组,而不是创建新的对象
System.out.println(sb.toString()); // 输出 "Hello World"
  • StringBuilderappend() 方法会将新字符串的字符添加到内部字符数组的末尾。如果当前数组容量不足,则扩展数组容量,再进行追加操作。

内部实现原理(伪代码简化):

public StringBuilder append(String str) {
    if (str == null) {
        str = "null";
    }
    int newCount = count + str.length(); // 计算新字符串需要的总长度
    if (newCount > value.length) {
        expandCapacity(newCount); // 如果容量不足,扩展数组
    }
    str.getChars(0, str.length(), value, count); // 将新字符串复制到字符数组
    count = newCount; // 更新字符数组的使用长度
    return this;
}
  • expandCapacity(newCount):当字符数组容量不足时,StringBuilderStringBuffer 会调用此方法扩展数组容量。通常,新容量会比当前容量大约扩大为 当前容量的两倍加上 2,以避免频繁的数组扩展操作。

3. StringBuilderStringBuffer 的区别

虽然 StringBuilderStringBuffer 的底层实现方式非常相似(都是基于可变字符数组的动态扩展),但它们之间有一个重要的区别:

  • StringBuffer 是线程安全的:StringBuffer 的方法是同步的(synchronized),因此在多线程环境下,多个线程可以安全地对同一个 StringBuffer 实例进行操作。每个方法调用都会获取一个锁,保证线程安全。

  • StringBuilder 不是线程安全的:StringBuilder 的方法没有同步,因此在单线程环境下,它的性能比 StringBuffer 更高,因为它避免了不必要的锁定开销。StringBuilder 更适合在单线程场景中使用。

4. 可变性与性能的关系

由于 StringBuilderStringBuffer 是可变的,它们的设计大大减少了内存消耗和提升了性能,尤其在大量修改字符串的场景中,例如在循环中拼接字符串。String 的不可变性意味着每次字符串修改都要创建新的对象,而 StringBuilderStringBuffer 则是在原有字符数组上进行修改,从而避免了额外的对象创建。

性能对比:

// String 不可变的操作,每次都会创建新的字符串对象
String str = "";
for (int i = 0; i < 1000; i++) {
    str += i;  // 由于 String 是不可变的,每次拼接都会创建新的对象
}

// StringBuilder 可变操作,直接在原有字符数组上修改
StringBuilder sb = new StringBuilder();
for (int i = 0; i < 1000; i++) {
    sb.append(i);  // 直接在同一个对象的字符数组上追加
}
  • 在第一个例子中,String 每次拼接都会创建一个新的字符串对象,效率低且内存消耗高。
  • 在第二个例子中,StringBuilder 直接在原字符数组上修改,性能更高,且内存消耗较少。

5. 总结

  • StringBuilderStringBuffer 的可变性:它们的可变性通过底层的 动态字符数组 实现。在字符串内容改变时,直接在内部数组上进行修改,而不需要创建新的对象。随着内容的增加,字符数组会自动扩展以适应更多字符。
  • StringBuffer 是线程安全的,而 StringBuilder 不是:StringBuffer 的方法通过同步机制保证线程安全,但在单线程环境下,StringBuilder 由于不需要同步,性能更好。
  • 性能优势:由于 StringBuilderStringBuffer 可以在同一个对象上进行多次修改,它们在频繁修改字符串时性能显著优于 String

在开发中,如果需要频繁地修改字符串,建议使用 StringBuilder(单线程)或 StringBuffer(多线程),这样可以大幅提高程序的性能。

posted @   Duancf  阅读(111)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· TypeScript + Deepseek 打造卜卦网站:技术与玄学的结合
· 阿里巴巴 QwQ-32B真的超越了 DeepSeek R-1吗?
· 【译】Visual Studio 中新的强大生产力特性
· 【设计模式】告别冗长if-else语句:使用策略模式优化代码结构
· 10年+ .NET Coder 心语 ── 封装的思维:从隐藏、稳定开始理解其本质意义
点击右上角即可分享
微信分享提示