views:

51

answers:

3

Simply I am trying to figure out what is the fast way to assign a value like

somevar+=1;

or

somevar=somevar+1;

time ago in situations with text instead of integers I encountered some performance decrease using text+="sometext" instead of text.append("sometext") or text=text+"sometext", the problem is that I did not find anymore the source code where I annotated my considerations. So theorically what's the fastest way?

The code background is set into a fast loop, nearly real time.

+2  A: 

Strings are immutable. Hence, concatenating 2 Strings causes a new String object to be created. Using StringBuilder will gain you a lot of performance.

However, primitive numeric types are not immutable and not heap allocated. Whatever you do with them tends to be really fast no matter what :)

jvdneste
Thank You! really interesting answer
Steel Plume
Concatenating 2 `String` s with `+` is really expensive and should be avoided, especially in loops. Make sure to use primitive types such as `int` instead of `Integer` wherever you can.
f1sh
@f1sh: Actually, string concatenation with `+` is in many cases not more expensive than `StringBuilder`. I'm sure *you* know this but it's always worth pointing out for all premature optimizers out there: The main reason for using `StringBuilder` instead of `+` is if you're doing non-one-liners to build up a string for some reason, like when in a loop or if the code is really long. Saying `new StringBuilder("Feces occurred: (").append(errMsg).append(", code = ").append(errCode).append(")").toString()` is in no way faster than doing the same thing with `+`, just far less readable.
gustafc
+4  A: 

If you have something like this:

Collection<String> strings = ...;
String all = "";
for (String s : strings) all += s;

... then it's equivalent to:

Collection<String> strings = ...;
String all = "";
for (String s : strings) all = new StringBuilder(all).append(s).toString();

Each loops creates a new StringBuilder which is essentially a copy of all, appends a copy of s to it, and then copies the result of the concatenation to a new String. Obviously, using a single StringBuilder saves a lot of unnecessary allocations:

Collection<String> strings = ...;
StringBuilder sb = new StringBuilder();
for (String s : strings) sb.append(s);
String all = sb.toString();

As for x += y versus x = x + y, they compile to the same thing.

class Concat {

        public String concat1(String a, String b) {
                a += b;
                return a;
        }

        public String concat2(String a, String b) {
                a = a + b;
                return a;
        }

}

Compile it with javac, and then disassemble it with javap:

$ javap -c Concat
Compiled from "Concat.java"
class Concat extends java.lang.Object{
Concat();
  Code:
   0:   aload_0
   1:   invokespecial   #1; //Method java/lang/Object."<init>":()V
   4:   return

public java.lang.String concat1(java.lang.String, java.lang.String);
  Code:
   0:   new     #2; //class java/lang/StringBuilder
   3:   dup
   4:   invokespecial   #3; //Method java/lang/StringBuilder."<init>":()V
   7:   aload_1
   8:   invokevirtual   #4; //Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
   11:  aload_2
   12:  invokevirtual   #4; //Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
   15:  invokevirtual   #5; //Method java/lang/StringBuilder.toString:()Ljava/lang/String;
   18:  astore_1
   19:  aload_1
   20:  areturn

public java.lang.String concat2(java.lang.String, java.lang.String);
  Code:
   0:   new     #2; //class java/lang/StringBuilder
   3:   dup
   4:   invokespecial   #3; //Method java/lang/StringBuilder."<init>":()V
   7:   aload_1
   8:   invokevirtual   #4; //Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
   11:  aload_2
   12:  invokevirtual   #4; //Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
   15:  invokevirtual   #5; //Method java/lang/StringBuilder.toString:()Ljava/lang/String;
   18:  astore_1
   19:  aload_1
   20:  areturn

}

Personally, I'd favor += because with it, you make a clearer statement of intent - "I want to add the content of b to a". Any variations in performance between the two forms are with a 100% certainty the result of something outside of your code (e.g., GC pauses, random cache misses or something like it).

Bad compilers might also have a slightly easier time to optimize the += form (which is irrelevant to you, since even if javac would be crappy, HotSpot sure isn't).

gustafc
+1 for inspection of bytecode.
f1sh
+1  A: 

String 'arithmetics' internal are rather complicated - just because they happen quite often and the compilers tries to optimize whereever it can.

First of all, append("something") is not a String method. It is a method from the StringBuffer or StringBuilder class.

The other expressions are quite equivalent. I'm pretty sure one can't look at the pattern and decide that one is faster in general.

In General and because Strings are immutable, a concatenation will create a new String for each String literal, a new String for each intermediate result and one new String for the result. So this

String s = "one" + "two" + "three";

will require five String objects ("one", "two", "three", "onetwo", "onwtwothree").

The first compiler optimization is 'interning' the literals. In short: "one", "two" and "three" are not created but reused.

A second compiler optimization is replacing the String addition by StringBuilder operation (if it makes sense). So the concatenation could be replaced by

StringBuilder sb = new StringBuilder("one");
sb.append("two");
sb.append("three");
String s = sb.toString();

(just to illustrate - a real compiler might not use the strategy for this example code, it might concatenate the the literals during compilation already...)

Andreas_D