题 StringBuilder和StringBuffer之间的区别


两者之间有什么主要区别 StringBuffer 和 StringBuilder? 在决定其中任何一个时,是否存在任何性能问题?


1323
2017-12-10 04:34


起源


查看这篇文章: StringBuilder与StringBuffer - Hussein Terek


答案:


StringBuffer 是同步的, StringBuilder 不是。


1452
2017-12-10 04:36



和StringBuilder旨在替代不需要同步的StringBuffer - Joel
几乎从不需要同步。如果有人想在StringBuilder上进行同步,他们可以在实例上使用synchronized(sb){}包围整个代码块 - locka
@locka我认为StringBuffer永远不是一个好主意(除非你有一个需要它的API) vanillajava.blogspot.de/2013/04/... - Peter Lawrey
只有我看到的StringBuffer才是输出和各种日志实用程序之类的控制台:许多线程可能会冲突输出。因为你不希望2输出混淆...但是通常在StringBuffer级别的同步太低级别,你会想要在像levelm这样的appender同步,所以locka的答案是最好的,而StringBuffer应该被弃用。这将节省新手的代码审查时间。 - Remi Morin
混合这两者的好助记符 - BuFFer是第一个,更老,因此同步实现。较新的Builder类使用Builder模式并且是异步的。 - Datageek


StringBuilder 比...更快 StringBuffer 因为它不是 synchronized

这是一个简单的基准测试:

public class Main {
    public static void main(String[] args) {
        int N = 77777777;
        long t;

        {
            StringBuffer sb = new StringBuffer();
            t = System.currentTimeMillis();
            for (int i = N; i --> 0 ;) {
                sb.append("");
            }
            System.out.println(System.currentTimeMillis() - t);
        }

        {
            StringBuilder sb = new StringBuilder();
            t = System.currentTimeMillis();
            for (int i = N; i > 0 ; i--) {
                sb.append("");
            }
            System.out.println(System.currentTimeMillis() - t);
        }
    }
}

一个 测试运行 给出了数字 2241 ms 对于 StringBuffer VS 753 ms 对于 StringBuilder


664
2018-05-05 09:15



我把字符串文字改成了更大的东西:“快速的棕色狐狸”,并得到了更有趣的结果。基本上,它们的速度一样快。我实际上已经没有内存,所以我不得不删除几个七人组。说明:热点优化了同步。您基本上只是测量热点执行此操作所需的时间(可能还有一些更优化)。 - Jilles van Gurp
你需要先热身。这个测试对StringBuffer不公平。而且,如果它实际上附加了一些东西会很好。实际上,我翻了测试,并附加了一个随机字符串并得到相反的测试。可以说,那个人不能相信简单的基准。相反的表明StringBuffer更快。 StringBuilder为5164,StringBuffer为3699 hastebin.com/piwicifami.avrasm - momo
这是我第一次看到 --> 0 在一个循环中。我花了一点时间才意识到这意味着什么。这是实际在实践中使用的东西,而不是通常的东西 ...; i > 0; i-- 句法? - Raimund Krämer
那 i --> 是非常恼人的语法方式...我认为这是一个箭头,因为有关ASCII艺术的评论。 - Sameer Puri
其他人得出的结论不同: alblue.bandlem.com/2016/04/jmh-stringbuffer-stringbuilder.html。基准测试应该用JMH完成,而不是简单 main() 此外,您的基准是不公平的。没有热身。 - Lukas Eder


基本上, StringBuffer 方法是同步的 StringBuilder 不是。

操作“几乎”相同,但在单个线程中使用同步方法是过度的。

这几乎就是它。

引用自 StringBuilder API

这个类[StringBuilder]提供了一个与StringBuffer兼容的API, 但不保证同步。此类设计用作StringBuffer的替代品,用于单个线程使用字符串缓冲区的位置(通常情况下)。在可能的情况下,建议优先使用此类作为StringBuffer 在大多数实现中它会更快。

所以它是替代它。

同样的事情发生在 Vector 和 ArrayList


228
2017-12-10 04:37



还有 Hashtable 和 HashMap。 - shmosel


但是需要借助一个例子来获得明显的差异?

StringBuffer或StringBuilder

简单地使用 StringBuilder 除非你真的想在线程之间共享一个缓冲区。 StringBuilder 是原始同步的未同步(开销较小=效率更高)的弟弟 StringBuffer 类。


151
2018-01-25 12:30



第一个好答案!!重点是“除非你在线程之间共享一个缓冲区” - AlexWien


首先让我们看看 相似之处: StringBuilder和StringBuffer都是可变的。这意味着您可以在同一位置更改它们的内容。

差异: StringBuffer也是可变的和同步的。 StringBuilder是可变的,但默认情况下不同步。

同步(同步)的含义: 当某些东西同步时,多个线程可以访问,并修改它而不会出现任何问题或副作用。 StringBuffer是同步的,因此您可以将它与多个线程一起使用而不会出现任何问题。

哪一个使用的时候? StringBuilder:当你需要一个可以修改的字符串时,只有一个线程正在访问和修改它。 StringBuffer:当你需要一个可以修改的字符串,并且多个线程正在访问和修改它。

注意 :不要不必要地使用StringBuffer,即如果只有一个线程正在修改和访问它,请不要使用它,因为它有很多锁定和解锁代码用于同步,这将不必要地占用CPU时间。除非有必要,否则不要使用锁。


74
2017-12-11 07:11



只想提一下StringBuffer的INDIVIDUAL方法调用是线程安全的。但是如果您有多行代码,请使用同步代码块来保证线程安全,使用一些锁定/监视器(按照惯例......)。基本上,不要只是假设使用线程安全库立即保证您的程序中的线程安全! - Kevin Lee


在单线程中, StringBuffer并不比StringBuilder慢得多,感谢JVM优化。在多线程中,您无法安全地使用StringBuilder。

这是我的测试:

public static void main(String[] args) {

    String withString ="";
    long t0 = System.currentTimeMillis();
    for (int i = 0 ; i < 100000; i++){
        withString+="some string";
    }
    System.out.println("strings:" + (System.currentTimeMillis() - t0));

    t0 = System.currentTimeMillis();
    StringBuffer buf = new StringBuffer();
    for (int i = 0 ; i < 100000; i++){
        buf.append("some string");
    }
    System.out.println("Buffers : "+(System.currentTimeMillis() - t0));

    t0 = System.currentTimeMillis();
    StringBuilder building = new StringBuilder();
    for (int i = 0 ; i < 100000; i++){
        building.append("some string");
    }
    System.out.println("Builder : "+(System.currentTimeMillis() - t0));
}

结果:
字符串:319740
缓冲区: 23
建造者:7!

因此,构建器比Buffers更快,并且比字符串连接更快。 现在让我们使用一个 执行者 对于多个线程:

public class StringsPerf {

    public static void main(String[] args) {

        ThreadPoolExecutor executorService = (ThreadPoolExecutor) Executors.newFixedThreadPool(10);
        //With Buffer
        StringBuffer buffer = new StringBuffer();
        for (int i = 0 ; i < 10; i++){
            executorService.execute(new AppendableRunnable(buffer));
        }
        shutdownAndAwaitTermination(executorService);
        System.out.println(" Thread Buffer : "+ AppendableRunnable.time);

        //With Builder
        AppendableRunnable.time = 0;
        executorService = (ThreadPoolExecutor) Executors.newFixedThreadPool(10);
        StringBuilder builder = new StringBuilder();
        for (int i = 0 ; i < 10; i++){
            executorService.execute(new AppendableRunnable(builder));
        }
        shutdownAndAwaitTermination(executorService);
        System.out.println(" Thread Builder: "+ AppendableRunnable.time);

    }

   static void shutdownAndAwaitTermination(ExecutorService pool) {
        pool.shutdown(); // code reduced from Official Javadoc for Executors
        try {
            if (!pool.awaitTermination(60, TimeUnit.SECONDS)) {
                pool.shutdownNow();
                if (!pool.awaitTermination(60, TimeUnit.SECONDS))
                    System.err.println("Pool did not terminate");
            }
        } catch (Exception e) {}
    }
}

class AppendableRunnable<T extends Appendable> implements Runnable {

    static long time = 0;
    T appendable;
    public AppendableRunnable(T appendable){
        this.appendable = appendable;
    }

    @Override
    public void run(){
        long t0 = System.currentTimeMillis();
        for (int j = 0 ; j < 10000 ; j++){
            try {
                appendable.append("some string");
            } catch (IOException e) {}
        }
        time+=(System.currentTimeMillis() - t0);
    }
}

现在StringBuffers采取 157毫秒 100000追加。这不是相同的测试,但与之前的37毫秒相比,你可以放心地假设 使用多线程时StringBuffers追加速度较慢。原因是JIT / hotspot / compiler / something在检测到存在时会进行优化 没有 需要检查锁。

使用StringBuilder,您有java.lang.ArrayIndexOutOfBoundsException,因为并发线程试图添加不应该的东西。

结论是您不必追逐StringBuffers。在尝试获得几纳秒之前,在有线程的地方,考虑一下他们在做什么。


42
2017-11-15 16:20



你忘了做“t0 = System.currentTimeMillis();”在做StringBuilder测试之前。因此,为StringBuilder显示的数字实际上是运行stringbuffer和stringbuilder测试所花费的时间。添加此行,您将看到StringBuilder在两倍时间内更快。 - Gena Batsyan
纠正了,谢谢。显然,它改变了一些东西。 - Nicolas Zozol
注意 withString+="some string"+i+" ; "; 不等于其他两个循环,因此不公平比较。 - Dave Jarvis
确切,纠正。思想字符串仍然很慢。 - Nicolas Zozol
你能解释一下为什么会这样吗? ArrayIndexOutOfBoundsException异常 提高 StringBuilder的 - Alireza Fattahi


StringBuilder是在Java 1.5中引入的,因此它不适用于早期的JVM。

来自 的Javadoc

StringBuilder类提供与StringBuffer兼容的API,但不保证同步。此类设计用作StringBuffer的替代品,用于单个线程使用字符串缓冲区的位置(通常情况下)。在可能的情况下,建议首先使用此类优先于StringBuffer,因为在大多数实现中它会更快。


37
2017-12-10 04:38



1.4处于使用寿命终点,因此在1.5之前几乎不值得担心。 - Tom Hawtin - tackline
@ tomHawtin-tackline不一定 - 我们大多数人每天都会使用1.4之前的企业产品。此外,BlackBerry java基于1.4,目前仍然非常新。 - Richard Le Mesurier
CDC和CLDC没有 StringBuilder。 - Jin Kwon