StringBuilder vs String concatenation in toString() in Java

后端 未结 18 2256
北恋
北恋 2020-11-21 04:18

Given the 2 toString() implementations below, which one is preferred:

public String toString(){
    return \"{a:\"+ a + \", b:\" + b + \", c: \"         


        
18条回答
  •  悲哀的现实
    2020-11-21 04:50

    For performance reasons, the use of += (String concatenation) is discouraged. The reason why is: Java String is an immutable, every time a new concatenation is done a new String is created (the new one has a different fingerprint from the older one already in the String pool ). Creating new strings puts pressure on the GC and slows down the program: object creation is expensive.

    Below code should make it more practical and clear at the same time.

    public static void main(String[] args) 
    {
        // warming up
        for(int i = 0; i < 100; i++)
            RandomStringUtils.randomAlphanumeric(1024);
        final StringBuilder appender = new StringBuilder();
        for(int i = 0; i < 100; i++)
            appender.append(RandomStringUtils.randomAlphanumeric(i));
    
        // testing
        for(int i = 1; i <= 10000; i*=10)
            test(i);
    }
    
    public static void test(final int howMany) 
    {
        List samples = new ArrayList<>(howMany);
        for(int i = 0; i < howMany; i++)
            samples.add(RandomStringUtils.randomAlphabetic(128));
    
        final StringBuilder builder = new StringBuilder();
        long start = System.nanoTime();
        for(String sample: samples)
            builder.append(sample);
        builder.toString();
        long elapsed = System.nanoTime() - start;
        System.out.printf("builder - %d - elapsed: %dus\n", howMany, elapsed / 1000);
    
        String accumulator = "";
        start = System.nanoTime();
        for(String sample: samples)
            accumulator += sample;
        elapsed = System.nanoTime() - start;
        System.out.printf("concatenation - %d - elapsed: %dus\n", howMany, elapsed / (int) 1e3);
    
        start = System.nanoTime();
        String newOne = null;
        for(String sample: samples)
            newOne = new String(sample);
        elapsed = System.nanoTime() - start;
        System.out.printf("creation - %d - elapsed: %dus\n\n", howMany, elapsed / 1000);
    }
    

    Results for a run are reported below.

    builder - 1 - elapsed: 132us
    concatenation - 1 - elapsed: 4us
    creation - 1 - elapsed: 5us
    
    builder - 10 - elapsed: 9us
    concatenation - 10 - elapsed: 26us
    creation - 10 - elapsed: 5us
    
    builder - 100 - elapsed: 77us
    concatenation - 100 - elapsed: 1669us
    creation - 100 - elapsed: 43us
    
    builder - 1000 - elapsed: 511us
    concatenation - 1000 - elapsed: 111504us
    creation - 1000 - elapsed: 282us
    
    builder - 10000 - elapsed: 3364us 
    concatenation - 10000 - elapsed: 5709793us
    creation - 10000 - elapsed: 972us
    

    Not considering the results for 1 concatenation (JIT was not yet doing its job), even for 10 concatenations the performance penalty is relevant; for thousands of concatenations, the difference is huge.

    Lessons learned from this very quick experiment (easily reproducible with the above code): never use the += to concatenate strings together, even in very basic cases where a few concatenations are needed (as said, creating new strings is expensive anyway and puts pressure on the GC).

提交回复
热议问题