23

I have the following code:

StringBuilder str = new StringBuilder("foo");
for(Field f : fields){
    str.append("|" + f);
}
str.append("|" + bar);
String result = str.toString();

I know compiler will optimize string concatenation "|" + f and replace it with StringBuilder. However will a new StringBuilder be created or the existing str will be used in Java 8? How about Java 9?

Anton Krosnev
  • 3,964
  • 1
  • 21
  • 37
  • 7
    I'm not 100% sure here but I'd doubt `str` will be reused. Why don't you just use `append("|").append(String.valueOf(f))` and be sure? – Thomas Jul 26 '17 at 10:46
  • 1
    Most likely a new one. But you can easily test it be looking into the compiled code. – Tom Jul 26 '17 at 10:47
  • 1
    Pretty sure a new one will be used – Philipp Jul 26 '17 at 10:47
  • 4
    @Thomas: there is no need for `String.valueOf(…)`. Just `append('|').append(f)` will do. – Holger Jul 26 '17 at 11:53
  • @Holger you're right, though I wasn't sure about that portion of the api and thus I added it to be sure ;) – Thomas Jul 26 '17 at 11:55
  • 2
    By the way, see https://stackoverflow.com/questions/63150/whats-the-best-way-to-build-a-string-of-delimited-items-in-java for better alternatives to replace your code. – Sulthan Jul 26 '17 at 17:50
  • I learned about agent technology. http://javabeat.net/introduction-to-java-agents/ Add an agent to your jvm and see how StringBuilder objects are created. Just add println to the constructor. – Yan Khonski Jul 26 '17 at 10:49

3 Answers3

23

By default in java-9 there will be no StringBuilder for string concatenation; it is a runtime decision how it's made via the invokedynamic. And the default policy is not a StringBuilder::append one.

You can also read more here.

Under java-8 a new one will be created (really easy to spot two occurrences of invokespecial // Method java/lang/StringBuilder."<init>":()V in the de-compiled bytecode.

Also, you have a suggestion about append.append...; just notice that this is much better than sb.append ... sb.append, and here is why.

Hearen
  • 7,420
  • 4
  • 53
  • 63
Eugene
  • 117,005
  • 15
  • 201
  • 306
  • 1
    Do you have any link that specifies that? It will be great to read about that. – Shirkam Jul 26 '17 at 11:04
  • 1
    @Shirkam just search String concatenation java-9 and plenty of results should pop up (also see my edit). – Eugene Jul 26 '17 at 11:06
  • 2
    @Shirkam read my other edit, that I think its pretty cool too. – Eugene Jul 26 '17 at 11:13
  • Why did you link to that meta-question? It's just how to work with answers. – Shirkam Jul 27 '17 at 04:46
  • 2
    @Shirkam: like with [lambda expressions](https://docs.oracle.com/javase/8/docs/api/?java/lang/invoke/LambdaMetafactory.html), there is a visible impact in the API, which is [already documented today](http://download.java.net/java/jdk9/docs/api/?java/lang/invoke/StringConcatFactory.html)… – Holger Jul 27 '17 at 18:38
8

As String concatenation optimization is performed by the Java compiler, you can see what it does by decompiling the byte code:

$ cat Test.java
interface Field {}

public class Test {

    static String toString(Field[] fields, Object bar) {
        StringBuilder str = new StringBuilder("foo");
        for(Field f : fields){
            str.append("|" + f);
        }
        str.append("|" + bar);
        return str.toString();
    }
}
$ javac Test.java
$ javap -c Test.class
Compiled from "Test.java"
public class stackoverflow.Test {
  public stackoverflow.Test();
    Code:
       0: aload_0
       1: invokespecial #8                  // Method java/lang/Object."<init>":()V
       4: return

  static java.lang.String toString(stackoverflow.Field[], java.lang.Object);
    Code:
       0: new           #16                 // class java/lang/StringBuilder
       3: dup
       4: ldc           #18                 // String foo
       6: invokespecial #20                 // Method java/lang/StringBuilder."<init>":(Ljava/lang/String;)V
       9: astore_2
      10: aload_0
      11: dup
      12: astore        6
      14: arraylength
      15: istore        5
      17: iconst_0
      18: istore        4
      20: goto          53
      23: aload         6
      25: iload         4
      27: aaload
      28: astore_3
      29: aload_2
      30: new           #16                 // class java/lang/StringBuilder
      33: dup
      34: ldc           #23                 // String |
      36: invokespecial #20                 // Method java/lang/StringBuilder."<init>":(Ljava/lang/String;)V
      39: aload_3
      40: invokevirtual #25                 // Method java/lang/StringBuilder.append:(Ljava/lang/Object;)Ljava/lang/StringBuilder;
      43: invokevirtual #29                 // Method java/lang/StringBuilder.toString:()Ljava/lang/String;
      46: invokevirtual #32                 // Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
      49: pop
      50: iinc          4, 1
      53: iload         4
      55: iload         5
      57: if_icmplt     23
      60: aload_2
      61: new           #16                 // class java/lang/StringBuilder
      64: dup
      65: ldc           #23                 // String |
      67: invokespecial #20                 // Method java/lang/StringBuilder."<init>":(Ljava/lang/String;)V
      70: aload_1
      71: invokevirtual #25                 // Method java/lang/StringBuilder.append:(Ljava/lang/Object;)Ljava/lang/StringBuilder;
      74: invokevirtual #29                 // Method java/lang/StringBuilder.toString:()Ljava/lang/String;
      77: invokevirtual #32                 // Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
      80: pop
      81: aload_2
      82: invokevirtual #29                 // Method java/lang/StringBuilder.toString:()Ljava/lang/String;
      85: areturn
}

As you can see, the code invokes StringBuilder constructors (Method java/lang/StringBuilder."<init>":) in 3 places, so new StringBuilders would be created in each iteration (unless the just-in-time compiler performs fancy optimization).

This is very unlikely to be a significant performance problem, but in the unlikely case it is you can easily fix this by rewriting to

str.append("|").append(f);
Hearen
  • 7,420
  • 4
  • 53
  • 63
meriton
  • 68,356
  • 14
  • 108
  • 175
  • 1
    Though this optimization is most likely implemented by all relevant Java compilers, there is in general no strong guarantee for this behavior. The JLS for Java 8 states: _"An implementation **may** choose to perform conversion and concatenation in one step to avoid creating and then discarding an intermediate String object. To increase the performance of repeated string concatenation, a Java compiler **may** use the StringBuffer class or a similar technique to reduce the number of intermediate String objects that are created by evaluation of an expression."_ – fxlae Jul 26 '17 at 19:47
0

As per the Java 9 API documentation

Implementation Note:

The implementation of the string concatenation operator is left to the discretion of a Java compiler, as long as the compiler ultimately conforms to The Java™ Language Specification. For example, the javac compiler may implement the operator with StringBuffer, StringBuilder, or java.lang.invoke.StringConcatFactory depending on the JDK version. The implementation of string conversion is typically through the method toString, defined by Object and inherited by all classes in Java.

According to this it will create a new String builder each iteration in your case. So, as mentioned by several people here, using the below code is more optimized

append("|").append(f)

You can find API Documentation here

Community
  • 1
  • 1
Manindar
  • 999
  • 2
  • 14
  • 30