StringBuilder vs String concatenation in toString() in Java

后端 未结 18 2183
北恋
北恋 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<String> 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).

    0 讨论(0)
  • 2020-11-21 04:54

    In Java 9 the version 1 should be faster because it is converted to invokedynamic call. More details can be found in JEP-280:

    The idea is to replace the entire StringBuilder append dance with a simple invokedynamic call to java.lang.invoke.StringConcatFactory, that will accept the values in the need of concatenation.

    0 讨论(0)
  • 2020-11-21 04:54

    There seems to be some debate whether using StringBuilder is still needed with current compilers. So I thought I'll give my 2 cents of experience.

    I have a JDBC result set of 10k records (yes, I need all of them in one batch.) Using the + operator takes about 5 minutes on my machine with Java 1.8. Using stringBuilder.append("") takes less than a second for the same query.

    So the difference is huge. Inside a loop StringBuilder is much faster.

    0 讨论(0)
  • 2020-11-21 04:57

    In most cases, you won't see an actual difference between the two approaches, but it's easy to construct a worst case scenario like this one:

    public class Main
    {
        public static void main(String[] args)
        {
            long now = System.currentTimeMillis();
            slow();
            System.out.println("slow elapsed " + (System.currentTimeMillis() - now) + " ms");
    
            now = System.currentTimeMillis();
            fast();
            System.out.println("fast elapsed " + (System.currentTimeMillis() - now) + " ms");
        }
    
        private static void fast()
        {
            StringBuilder s = new StringBuilder();
            for(int i=0;i<100000;i++)
                s.append("*");      
        }
    
        private static void slow()
        {
            String s = "";
            for(int i=0;i<100000;i++)
                s+="*";
        }
    }
    

    The output is:

    slow elapsed 11741 ms
    fast elapsed 7 ms
    

    The problem is that to += append to a string reconstructs a new string, so it costs something linear to the length of your strings (sum of both).

    So - to your question:

    The second approach would be faster, but it's less readable and harder to maintain. As I said, in your specific case you would probably not see the difference.

    0 讨论(0)
  • 2020-11-21 04:59

    Since Java 1.5, simple one line concatenation with "+" and StringBuilder.append() generate exactly the same bytecode.

    So for the sake of code readability, use "+".

    2 exceptions :

    • multithreaded environment : StringBuffer
    • concatenation in loops : StringBuilder/StringBuffer
    0 讨论(0)
  • 2020-11-21 05:00

    Performance wise String concatenation using '+' is costlier because it has to make a whole new copy of String since Strings are immutable in java. This plays particular role if concatenation is very frequent, eg: inside a loop. Following is what my IDEA suggests when I attempt to do such a thing:

    General Rules:

    • Within a single string assignment, using String concatenation is fine.
    • If you're looping to build up a large block of character data, go for StringBuffer.
    • Using += on a String is always going to be less efficient than using a StringBuffer, so it should ring warning bells - but in certain cases the optimisation gained will be negligible compared with the readability issues, so use your common sense.

    Here is a nice Jon Skeet blog around this topic.

    0 讨论(0)
提交回复
热议问题