请告诉我一个实时情况比较字符串,StringBuffer,和StringBuilder?


当前回答

注意,如果您使用的是Java 5或更新版本,您应该使用StringBuilder而不是StringBuffer。来自API文档:

从JDK 5发行版开始,这个类已经被一个为单个线程使用而设计的等效类StringBuilder所补充。通常应该优先使用StringBuilder类,因为它支持所有相同的操作,但速度更快,因为它不执行同步。

在实践中,您几乎永远不会同时从多个线程中使用它,因此StringBuffer所做的同步几乎总是不必要的开销。

其他回答

下面是一个小代码片段,演示了如何在异步环境中破坏StringBuilder

public static void main(String[] args) throws InterruptedException {
    StringBuilder builder = new StringBuilder();

    ExecutorService executorService = Executors.newFixedThreadPool(50);
    for (int i = 0; i < 1000 * 1000; i++) {
        executorService.submit(new AppendRunnable(builder));
    }

    executorService.shutdown();
    executorService.awaitTermination(1, TimeUnit.MINUTES);

    Stream.of(builder.toString().split(System.lineSeparator()))
        .filter(line -> !line.equals("I just appended this!"))
        .forEach(System.out::println);
}

record AppendRunnable(StringBuilder stringBuilder) implements Runnable {

    @Override
    public void run() {
        stringBuilder.append("I just appended this!\n");
    }
}

本质上,我们将字符串附加到构建器中,并期望它们都等于“我刚刚附加了这个!”。但它们不是,其中一些前缀是空字符,因为构建器的内部字符缓冲区没有同步,它被错误地调整了大小。在这种情况下使用StringBuffer可以解决问题。更多详细信息请点击这里

String和其他两个类的区别在于String是不可变的,而其他两个类是可变的。

但是为什么我们有两个相同目的的类呢?

原因是StringBuffer是线程安全的,而StringBuilder不是。 StringBuilder是StringBuffer Api上的一个新类,它是在JDK5中引入的,如果你工作在单线程环境中,总是推荐使用它,因为它要快得多

有关完整的详细信息,请访问http://www.codingeek.com/java/stringbuilder-and-stringbuffer-a-way-to-create-mutable-strings-in-java/

基础知识:

String是一个不可变的类,它不能被改变。 StringBuilder是一个可变类,可以追加,字符替换或删除,并最终转换为String StringBuffer是StringBuilder的原始同步版本

在只有一个线程访问对象的所有情况下,您都应该使用StringBuilder。

细节:

还要注意,StringBuilder/Buffers并不是魔法,它们只是使用一个数组作为支持对象,当数组满时必须重新分配。确保在初始时创建足够大的StringBuilder/Buffer对象,这样就不必在每次调用.append()时不断地调整它们的大小。

调整大小会变得非常糟糕。基本上,每次需要扩展时,它都会将支持数组的大小调整为当前大小的2倍。这可能导致在StringBuilder/Buffer类开始变大时分配大量RAM而不使用。

In Java String x = "A" + "B"; uses a StringBuilder behind the scenes. So for simple cases there is no benefit of declaring your own. But if you are building String objects that are large, say less than 4k, then declaring StringBuilder sb = StringBuilder(4096); is much more efficient than concatenation or using the default constructor which is only 16 characters. If your String is going to be less than 10k then initialize it with the constructor to 10k to be safe. But if it is initialize to 10k then you write 1 character more than 10k, it will get re-allocated and copied to a 20k array. So initializing high is better than to low.

在自动调整大小的情况下,在第17个字符时,后面的数组被重新分配并复制为32个字符,在第33个字符时,同样发生这种情况,你需要重新分配并将数组复制为64个字符。你可以看到这是如何退化为大量的重新分配和复制,这是你真正试图避免使用StringBuilder/Buffer在第一个地方。

这是来自JDK 6的AbstractStringBuilder源代码

   void expandCapacity(int minimumCapacity) {
    int newCapacity = (value.length + 1) * 2;
        if (newCapacity < 0) {
            newCapacity = Integer.MAX_VALUE;
        } else if (minimumCapacity > newCapacity) {
        newCapacity = minimumCapacity;
    }
        value = Arrays.copyOf(value, newCapacity);
    }

一个最好的做法是初始化StringBuilder/Buffer,如果你不知道String会有多大,但你可以猜测,比你认为你需要的要大一点。分配比您需要的稍微多一点的内存比大量的重新分配和复制要好。

另外,要注意用String初始化StringBuilder/Buffer,因为这样只会分配String + 16个字符的大小,在大多数情况下,这只会开始退化的重新分配和复制周期,而这是您试图避免的。下面是直接来自Java 6的源代码。

public StringBuilder(String str) {
    super(str.length() + 16);
    append(str);
    }

如果碰巧得到了一个不是自己创建的StringBuilder/Buffer实例,并且不能控制所调用的构造函数,有一种方法可以避免恶化的重新分配和复制行为。调用. ensurecapacity(),并设置你想要确保生成的字符串适合的大小。

的选择:

需要注意的是,如果您正在进行非常繁重的字符串构建和操作,有一种更面向性能的替代方法,称为rope。

另一种替代方法是通过子类化ArrayList<String>来创建一个StringList实现,并添加计数器来跟踪每个.append()和列表的其他突变操作上的字符数,然后重写. tostring()来创建一个你需要的精确大小的StringBuilder,并循环遍历列表并构建输出,你甚至可以使该StringBuilder成为一个实例变量并“缓存”. tostring()的结果,只有在发生变化时才需要重新生成它。

在构建固定格式化输出时也不要忘记String.format(),编译器可以优化它,因为它们使它更好。

You use String when an immutable structure is appropriate; obtaining a new character sequence from a String may carry an unacceptable performance penalty, either in CPU time or memory (obtaining substrings is CPU efficient because the data is not copied, but this means a potentially much larger amount of data may remain allocated). You use StringBuilder when you need to create a mutable character sequence, usually to concatenate several character sequences together. You use StringBuffer in the same circumstances you would use StringBuilder, but when changes to the underlying string must be synchronized (because several threads are reading/modifyind the string buffer).

这里有一个例子。

另外,StringBuffer是线程安全的,而StringBuilder不是。

因此,在实时情况下,当不同的线程正在访问它时,StringBuilder可能会有一个不确定的结果。