22

When i see something (pseudo 1-liner) like this:

str1 + "a" + str2

Is it much worse (or better/equal) than the following (pseudo 1-liner)?

str1 + 'a' + str2

Update: Better example (by @QPaysTaxes) to reduce confusion regarding my original example.

What i tried: Various stuff for the past 10 years programming Java but i never managed to realy see whats under the hood - e.g. i would assume the second is slightly "faster/better" because there is no String-Object(s) created for the slash-sign and/or the garbage collector of Java has to handle less. I once prepared for the Java Certificates and might would have been able to argue better back in that time but it seems even thus its my daily business the "theory" about Java must be keept up to date as well... I know without any better explanation than my assumptation that indexOf('c') should be used rather than indexOf("C") and i wondered if the same counts for String-concatenation.

I also googled a bit but as my title might imply i am not quite good to describe what i am looking for without a example. I am sorry for this and the possibility this handicap just produced a duplicate.

What i will try: Based on the accepted answer here String concatenation: concat() vs "+" operator i hope to be able to have a start to see whats under the hood and one day be able to argue/ answer such questions that profund.

Community
  • 1
  • 1
JBA
  • 2,769
  • 5
  • 24
  • 40
  • 3
    Even better would be to use File.separatorChar/File.separator to make things OS independent. – Necreaux Mar 04 '15 at 13:26
  • Or just use `new File(parent, filename)`... – Axel Mar 04 '15 at 13:28
  • 1
    Instead of using String concatenation operator, `+`, that creates a new `String` for each operation, use a `StringBuilder`. – riccardo.cardin Mar 04 '15 at 13:29
  • @Axel Its not directly about the file (maybe that has been a bad example lol) its more about if i should add a single `Character` using the double (add the `Character` as String) or single quotes (add the `Character` as `Character`) ... (i am not sure if it in the end matters) – JBA Mar 04 '15 at 13:30
  • @riccardo.cardin so regarding the StringBuilder and a single _Character_ should i rather use `append("C")` or `append('c')` – JBA Mar 04 '15 at 13:32
  • Try it and see. Use *javap* to print out the bytecodes. – Hot Licks Mar 04 '15 at 13:39
  • 1
    @JBA, looking at StringBuilder code, I guess that using `append('c')` should be more performant than using `append("C")`. `StringBuilder`'s internal representation uses a `char[]`, so if you use a `String`, the method has to apply some kind of conversion. Functionally speaking, the two methods are equal in my opinion. – riccardo.cardin Mar 04 '15 at 13:40
  • 1
    As an alternative example, you could simply compare `str1 + 'a' + str2` to `str1 + "a" + str2`; that keeps it conceptual and avoids anyone suggesting answers based on implied usage. – Nic Mar 04 '15 at 13:40

7 Answers7

15

Based on the accepted answer here I hope to be able to have a start to see whats under the hood.

Let's have a look at the generated bytecode when concatenating a String with a Character:

String str1 = "a" + "test";
String str2 = 'a' + "test";

0: ldc           #2                  // String atest
2: astore_1
3: ldc           #2                  // String atest
5: astore_2

as you can see, there is no difference, the compiler will convert it to the same bytecode.

Now let's have a look at the generated bytecode when concatenating a Character to a String variable.

String str1 = "a" + str3; //str3 is a String
String str2 = 'a' + str3; 

 7: invokespecial #4                  // Method java/lang/StringBuilder."<init>":()V
10: ldc           #5                  // String a
12: invokevirtual #6                  // Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
15: aload_1
16: invokevirtual #6                  // Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
19: invokevirtual #7                  // Method java/lang/StringBuilder.toString:()Ljava/lang/String;
22: astore_2
23: new           #3                  // class java/lang/StringBuilder
26: dup
27: invokespecial #4                  // Method java/lang/StringBuilder."<init>":()V
30: bipush        97
32: invokevirtual #8                  // Method java/lang/StringBuilder.append:(C)Ljava/lang/StringBuilder;
35: aload_1
36: invokevirtual #6                  // Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
39: invokevirtual #7                  // Method java/lang/StringBuilder.toString:()Ljava/lang/String;

As you can see, there is a little difference.

10: ldc           #5                  // String a

ldc push a constant #index from a constant pool (String, int or float) onto the stack.

Therefore, if you are concatenating directly with a variable, concatenating a Character will generate less bytecode, that is what is under the hood.

Now for the performance issue, this wont represent any signifiant performance difference as the JIT compiler optimize most of the temporary objects, unless you specified when running your program to disable the JIT compiler using -Djava.compiler=NONE.

Jean-François Savard
  • 20,626
  • 7
  • 49
  • 76
  • 1
    Thank you very much for your reply. May i ask if you use "javap -c" to get such informations? (I am still at work and cant currently try it my own :/ ) – JBA Mar 04 '15 at 14:33
  • 1
    @JBA javap -c would work, however I prefer using "javap -v" to get all informations. – Jean-François Savard Mar 04 '15 at 14:43
  • 1
    Any idea why Java uses a `StringBuilder` when concatenating a whopping total of two things? I don't think there is any combination of two items for which `StringBuilder` would be better than `(s1 == null ? "" : s1).concat(s2)`. If the class library added a few static `concat` methods to `String` which took 2, 3, or 4 arguments, then `StringBuilder` would only "win" when concatenating more than 16 strings, and even with 16 or more items could be outperformed in both code size and speed by a `String` constructor that took a `String[]` argument. – supercat Mar 04 '15 at 22:02
12

I prefer to use "a" instead of 'a' to make sure the result is a String.

Consider this:

public static void main(String... args) {
    String s = "foo";
    int i = 1;
    Object arg = s + '/' + i;
    log(arg);
}

private static void log(Object... args) {
    MessageFormat format = new MessageFormat("bar {0}");
    String message = format.format(args);
    System.out.println(message); // or write to a log or something
}

Assume you decide you don’t need s in the message anymore and change the third line in the main method to:

Object arg = '/' + i;

Then arg will contain just a number, because char + int does not concatenate, but add the values.

Martin
  • 2,573
  • 28
  • 22
4

If you construct a filename you sure will use it afterwards. That in most cases involves access to a physical media which is magnitudes slower than anything you can do wrong with concatenating your Strings. So, do what is maintable and don't worry about performance in this particular case.

My advice when building filenames is to use the File class or Path that will automatically make sure to get path separators right.

EDIT: As you point out in your comment, your question is about the general case. Just look at the source. StringBuilder.append(String) ends up doing a System.arraycopy() in String.getChars() whilst StringBuilder.append(char) directly copies a single character. So in theory, StringBuilder.append(char) will be faster.

However, you'd have to benchmark this to see if it makes any difference in practice.

Axel
  • 13,939
  • 5
  • 50
  • 79
3

I'm not sure if either of the options is better in terms of performance, but I can think of another issue to consider, that would prefer the first snippet.

The compiler can better protect you against typos if you append primitives instead of the String representation of those primitives.

Consider:

String plus10 = "plus" + 10;

If you type by mistake

String plus10 =  "plus" + 1O;

The compiler will give you an error.

If, on the other hand, you type

String plus10 =  "plus" + "1O";

The compiler will have no problem with that.

The same goes for appending chars

String plus = "x" + '++' + "y";

will not compile while

String plus = "x" + "++" +  "y";

will pass compilation.

Of course it would be better to use constants and not hard coded values (and to append to a StringBuilder instead of using String concatenation), but even for the constants I would prefer primitive types over Strings, as they give you one more level of protection against errors.

Eran
  • 387,369
  • 54
  • 702
  • 768
3

There is no any significant difference in performance actually. An average it will take the same time to do string concatenation.

However, internally Java compiler replaces + operator with StringBuilder at compile time.

So when using + operator with char, compiler will convert it into a StringBuilder internally and use .append(char). The same will happen with a string, with a difference that it will use .append(String).

And as I mentioned above, there is no difference an average. Simple test will show that time difference is close to 0. So this is really matter of readability. And from readability perspective, if you are concentrating strings, it's better to keep the type same, and use String even for single characters, rather than char

vtor
  • 8,989
  • 7
  • 51
  • 67
  • Here is a topic http://stackoverflow.com/questions/11408427/how-does-the-string-class-override-the-operator where + operator is discussed. – vtor Mar 04 '15 at 13:54
3

Looking at the source code often helps to understand what is happening.

String s = s1 + s2

Will execute:

String s = new StringBuilder(s1).append(s2).toString();

Now look into the source code for append(char) and append(string) of the class StringBuilder:

http://grepcode.com/file/repository.grepcode.com/java/root/jdk/openjdk/6-b14/java/lang/AbstractStringBuilder.java#AbstractStringBuilder.append%28char%29

http://grepcode.com/file/repository.grepcode.com/java/root/jdk/openjdk/6-b14/java/lang/AbstractStringBuilder.java#AbstractStringBuilder.append%28java.lang.String%29

You will see that append(string) performs more checks to see if the string is null or empty. However, you probably will not notice any difference.

MarcelSimon
  • 572
  • 1
  • 4
  • 6
2

This is what's under the hood: String str = s1 + "/"; essentially creates 2 new separate String objects (str and new String("/")).

This is no problem for small software, but think about it memory-wise if you were to create 2 String objects (keep in mind: objects reserve 1 entry in the Stack plus contents kept in the Heap) for n > 500.000 database entries.

Using single quotes, like String str = s1 + '/', will result in another process entirely. '/' stands for the numeric ASCii character representation value of whatever single character is written between the quotes. This operation has a constant (O(1)) runtime (think instant array access) and will naturally be faster than creating and referencing objects.

As lots of people have suggested already, using a StringBuilder object for String concatenation is much easier on memory than building strings with the + operator.

bhnn
  • 225
  • 5
  • 17
  • 2
    String literals are pooled so referencing a literal only ever creates a single object. Also Java `char` is UTF16. – Radiodef Mar 05 '15 at 01:15