Is it better practice to use String.format over string Concatenation in Java?
Asked Answered
N

16

350

Is there a perceptible difference between using String.format and String concatenation in Java?

I tend to use String.format but occasionally will slip and use a concatenation. I was wondering if one was better than the other.

The way I see it, String.format gives you more power in "formatting" the string; and concatenation means you don't have to worry about accidentally putting in an extra %s or missing one out.

String.format is also shorter.

Which one is more readable depends on how your head works.

Nonconformity answered 29/5, 2009 at 10:51 Comment(1)
I think we can go with MessageFormat.format. Please see the answer https://mcmap.net/q/89002/-is-it-better-practice-to-use-string-format-over-string-concatenation-in-java for more info.Tempera
S
285

I'd suggest that it is better practice to use String.format(). The main reason is that String.format() can be more easily localised with text loaded from resource files whereas concatenation can't be localised without producing a new executable with different code for each language.

If you plan on your app being localisable you should also get into the habit of specifying argument positions for your format tokens as well:

"Hello %1$s the time is %2$t"

This can then be localised and have the name and time tokens swapped without requiring a recompile of the executable to account for the different ordering. With argument positions you can also re-use the same argument without passing it into the function twice:

String.format("Hello %1$s, your name is %1$s and the time is %2$t", name, time)
Schipperke answered 29/5, 2009 at 10:56 Comment(2)
Can you point me to some documentation that talks about how to work with argument positions/order in Java (i.e., how to reference arguments by their position)? Thanks.Abbate
Better late than never, random Java version: docs.oracle.com/javase/1.5.0/docs/api/java/util/…Oech
C
247

About performance:

public static void main(String[] args) throws Exception {      
  long start = System.currentTimeMillis();
  for(int i = 0; i < 1000000; i++){
    String s = "Hi " + i + "; Hi to you " + i*2;
  }
  long end = System.currentTimeMillis();
  System.out.println("Concatenation = " + ((end - start)) + " millisecond") ;

  start = System.currentTimeMillis();
  for(int i = 0; i < 1000000; i++){
    String s = String.format("Hi %s; Hi to you %s",i, + i*2);
  }
  end = System.currentTimeMillis();
  System.out.println("Format = " + ((end - start)) + " millisecond");
}

The timing results are as follows:

  • Concatenation = 265 millisecond
  • Format = 4141 millisecond

Therefore, concatenation is much faster than String.format.

Contemptible answered 18/1, 2010 at 16:58 Comment(8)
They are all bad practice. Use StringBuilder.Myrna
StringBuilder is out of scope here (the OP question was about comparing String.format over string Concatenation) but have you performace data about String Builder?Contemptible
@AmirRaminar: The compiler converts "+" to calls to StringBuilder automatically.Disturbance
@MartinSchröder: If you run javap -c StringTest.class you'll see that the compiler converts "+" to StringBuilder automatically only if you are not in a loop. If the concatenation is all done on a single line it's the same as using '+', but if you use myString += "morechars"; or myString += anotherString; on multiple lines you'll notice that more than one StringBuilder might be created, so using "+" is not always as efficient as StringBuilder.Philippine
@Philippine I agree with all that follows your first statement. But your first statement is wrong: the compiler does always convert + to StringBuilder, even in loops. That's precisely what you said later on.Bimonthly
@Joffrey: what I meant was that for loops + does not get converted to StringBuilder.append() but instead a new StringBuilder() happens on each iteration.Philippine
I think we can go with MessageFormat.format. Please see the answer https://mcmap.net/q/89002/-is-it-better-practice-to-use-string-format-over-string-concatenation-in-java for more info.Tempera
When comparing String.format with concatenation, then only on StringBuilder is used, there is no loop. For loops, definitely use explicit StringBuilder, but that's completely different use case than String.format-like work.Featurelength
S
61

One problem with .format is that you lose static type safety. You can have too few arguments for your format, and you can have the wrong types for the format specifiers - both leading to an IllegalFormatException at runtime, so you might end up with logging code that breaks production.

In contrast, the arguments to + can be tested by the compiler.

The security history of (on which the format function is modeled) is long and frightening.

Sukin answered 23/2, 2012 at 14:56 Comment(2)
just for the record, modern IDEs (e.g. IntelliJ) assist in arguments count and type matchingBalaam
Good point about compilation, I recommend you do these checks via FindBugs (which can run in the IDE or via Maven during the build), note that this will also check formatting in all of your logging ! This works regardless of the users IDEDaglock
E
47

Since there is discussion about performance I figured I'd add in a comparison that included StringBuilder. It is in fact faster than the concat and, naturally the String.format option.

To make this a sort of apples to apples comparison I instantiate a new StringBuilder in the loop rather than outside (this is actually faster than doing just one instantiation most likely due to the overhead of re-allocating space for the looping append at the end of one builder).

    String formatString = "Hi %s; Hi to you %s";

    long start = System.currentTimeMillis();
    for (int i = 0; i < 1000000; i++) {
        String s = String.format(formatString, i, +i * 2);
    }

    long end = System.currentTimeMillis();
    log.info("Format = " + ((end - start)) + " millisecond");

    start = System.currentTimeMillis();

    for (int i = 0; i < 1000000; i++) {
        String s = "Hi " + i + "; Hi to you " + i * 2;
    }

    end = System.currentTimeMillis();

    log.info("Concatenation = " + ((end - start)) + " millisecond");

    start = System.currentTimeMillis();

    for (int i = 0; i < 1000000; i++) {
        StringBuilder bldString = new StringBuilder("Hi ");
        bldString.append(i).append("; Hi to you ").append(i * 2);
    }

    end = System.currentTimeMillis();

    log.info("String Builder = " + ((end - start)) + " millisecond");
  • 2012-01-11 16:30:46,058 INFO [TestMain] - Format = 1416 millisecond
  • 2012-01-11 16:30:46,190 INFO [TestMain] - Concatenation = 134 millisecond
  • 2012-01-11 16:30:46,313 INFO [TestMain] - String Builder = 117 millisecond
Epideictic answered 11/1, 2012 at 22:47 Comment(11)
The StringBuilder test doesn't call toString(), so it isn't a fair comparison. I suspect you'll find it's within measurement error of the performance of concatenation if you fix that bug.Feuar
Hi Jamey, I'm afraid I'm not following you here, can you please elaborate? I don't think any of the tests explicitly call toString() so I'm not sure why I should add that for comparions sake; I think I am missing something here. - TripEpideictic
In the concatenation and format tests, you asked for a String. The StringBuilder test, to be fair, needs a final step that turns the StringBuilder's contents into a String. You do that by calling bldString.toString(). I hope that explains it?Feuar
Jamey Sharp is exactly right. Invoking bldString.toString() is about the same if not slower than string concatenation.Renny
It is still pretty fast (see below for test with .toString() added, and this is only for 3 concatenations. I am pretty sure that for 4 or more the string builder would be considerably faster, after all that is what it was designed for :)Recommit
String s = bldString.toString(); The timings were with concatenation and stringbuilder almost on par with each other: Format = 1520 millisecond , Concatenation = 167 millisecond, String Builder = 173 millisecond I ran them in a loop and averaged each out to get a good rep: (pre-jvm optimization, will try a 10000+ loop when I get time)Epideictic
I added the assignment of the toString() at the bottom of the loop.Epideictic
@Recommit I suggest you read the byte code of the StringBuilder and + version. You'll realize your mistake: they produce the same bytecode. + concatenations are converted to StringBuilder concatenations by the compiler. So there can't be any performance difference, since the JVM is literally executing the same thing. The only difference you will find is when you reuse the same StringBuilder manually in multiple concatenations (e.g. using 1 StringBuilder for a loop containing a concatenation, instead of +=), but that is not the case in this benchmark.Bimonthly
That being said, + could in fact become faster in the future. If the Java language provides a new way of concatenating that is faster than StringBuilder, then the compilers could be updated to convert + differently and improve its performance, while any code using StringBuilder directly won't benefit from it. This is precisely what happened when StringBuilder appeared and replaced the slower StringBuffer.Bimonthly
@Bimonthly Thanks, I didn't realise they were now effectively the same, very interesting :)Recommit
How do you guys even know wether the code is executed at all? The variables are never read or used, you can't be sure that JIT doesn't remove this code in a first place.Veron
R
24

Which one is more readable depends on how your head works.

You got your answer right there.

It's a matter of personal taste.

String concatenation is marginally faster, I suppose, but that should be negligible.

Reisfield answered 29/5, 2009 at 10:56 Comment(4)
I agree. Thinking about performance differences here is mainly just premature optimisation - in the unlikely event that profiling shows there's a problem here, then worry about it.Commune
It's only really a matter of personal taste if the project is small and never intended to be internationalised in any meaningful sense. Otherwise String.format wins out over concatenation in every way.Schipperke
I disagree. No matter how large the project is, you're hardly going to localise every string that's ever constructed within it. In other words, it depends on the situation (what are the strings used for).Commune
I can't imagine how anybody would ever consider 'String.format("%s%s", a, b)' to be more readable than 'a+b', and given the order-of-magnitude difference in speed that answer seems clear to me (in situations which will not require localization such as debug or most logging statements).Sunnysunproof
J
21

Here's a test with multiple sample sizes in milliseconds.

public class Time {

public static String sysFile = "/sys/class/camera/rear/rear_flash";
public static String cmdString = "echo %s > " + sysFile;

public static void main(String[] args) {

  int i = 1;
  for(int run=1; run <= 12; run++){
      for(int test =1; test <= 2 ; test++){
        System.out.println(
                String.format("\nTEST: %s, RUN: %s, Iterations: %s",run,test,i));
        test(run, i);
      }
      System.out.println("\n____________________________");
      i = i*3;
  }
}

public static void test(int run, int iterations){

      long start = System.nanoTime();
      for( int i=0;i<iterations; i++){
          String s = "echo " + i + " > "+ sysFile;
      }
      long t = System.nanoTime() - start;   
      String r = String.format("  %-13s =%10d %s", "Concatenation",t,"nanosecond");
      System.out.println(r) ;


     start = System.nanoTime();       
     for( int i=0;i<iterations; i++){
         String s =  String.format(cmdString, i);
     }
     t = System.nanoTime() - start; 
     r = String.format("  %-13s =%10d %s", "Format",t,"nanosecond");
     System.out.println(r);

      start = System.nanoTime();          
      for( int i=0;i<iterations; i++){
          StringBuilder b = new StringBuilder("echo ");
          b.append(i).append(" > ").append(sysFile);
          String s = b.toString();
      }
     t = System.nanoTime() - start; 
     r = String.format("  %-13s =%10d %s", "StringBuilder",t,"nanosecond");
     System.out.println(r);
}

}

TEST: 1, RUN: 1, Iterations: 1
  Concatenation =     14911 nanosecond
  Format        =     45026 nanosecond
  StringBuilder =      3509 nanosecond

TEST: 1, RUN: 2, Iterations: 1
  Concatenation =      3509 nanosecond
  Format        =     38594 nanosecond
  StringBuilder =      3509 nanosecond

____________________________

TEST: 2, RUN: 1, Iterations: 3
  Concatenation =      8479 nanosecond
  Format        =     94438 nanosecond
  StringBuilder =      5263 nanosecond

TEST: 2, RUN: 2, Iterations: 3
  Concatenation =      4970 nanosecond
  Format        =     92976 nanosecond
  StringBuilder =      5848 nanosecond

____________________________

TEST: 3, RUN: 1, Iterations: 9
  Concatenation =     11403 nanosecond
  Format        =    287115 nanosecond
  StringBuilder =     14326 nanosecond

TEST: 3, RUN: 2, Iterations: 9
  Concatenation =     12280 nanosecond
  Format        =    209051 nanosecond
  StringBuilder =     11818 nanosecond

____________________________

TEST: 5, RUN: 1, Iterations: 81
  Concatenation =     54383 nanosecond
  Format        =   1503113 nanosecond
  StringBuilder =     40056 nanosecond

TEST: 5, RUN: 2, Iterations: 81
  Concatenation =     44149 nanosecond
  Format        =   1264241 nanosecond
  StringBuilder =     34208 nanosecond

____________________________

TEST: 6, RUN: 1, Iterations: 243
  Concatenation =     76018 nanosecond
  Format        =   3210891 nanosecond
  StringBuilder =     76603 nanosecond

TEST: 6, RUN: 2, Iterations: 243
  Concatenation =     91222 nanosecond
  Format        =   2716773 nanosecond
  StringBuilder =     73972 nanosecond

____________________________

TEST: 8, RUN: 1, Iterations: 2187
  Concatenation =    527450 nanosecond
  Format        =  10291108 nanosecond
  StringBuilder =    885027 nanosecond

TEST: 8, RUN: 2, Iterations: 2187
  Concatenation =    526865 nanosecond
  Format        =   6294307 nanosecond
  StringBuilder =    591773 nanosecond

____________________________

TEST: 10, RUN: 1, Iterations: 19683
  Concatenation =   4592961 nanosecond
  Format        =  60114307 nanosecond
  StringBuilder =   2129387 nanosecond

TEST: 10, RUN: 2, Iterations: 19683
  Concatenation =   1850166 nanosecond
  Format        =  35940524 nanosecond
  StringBuilder =   1885544 nanosecond

  ____________________________

TEST: 12, RUN: 1, Iterations: 177147
  Concatenation =  26847286 nanosecond
  Format        = 126332877 nanosecond
  StringBuilder =  17578914 nanosecond

TEST: 12, RUN: 2, Iterations: 177147
  Concatenation =  24405056 nanosecond
  Format        = 129707207 nanosecond
  StringBuilder =  12253840 nanosecond
Jehanna answered 22/10, 2014 at 7:6 Comment(2)
StringBuilder is absolutely the fastest method when you are appending characters in a loop, for example, when you want to create a string with one thousand 1's by adding them one by one. Here is more info: pellegrino.link/2015/08/22/…Smolder
I like it how you always use String.format for output :D so there is an advantage. and to be honest if we are not talking about millions of itterations, I prefer string.format for readability as your code shows the obvious advantage!Rhododendron
R
12

Here's the same test as above with the modification of calling the toString() method on the StringBuilder. The results below show that the StringBuilder approach is just a bit slower than String concatenation using the + operator.

file: StringTest.java

class StringTest {

  public static void main(String[] args) {

    String formatString = "Hi %s; Hi to you %s";

    long start = System.currentTimeMillis();
    for (int i = 0; i < 1000000; i++) {
        String s = String.format(formatString, i, +i * 2);
    }

    long end = System.currentTimeMillis();
    System.out.println("Format = " + ((end - start)) + " millisecond");

    start = System.currentTimeMillis();

    for (int i = 0; i < 1000000; i++) {
        String s = "Hi " + i + "; Hi to you " + i * 2;
    }

    end = System.currentTimeMillis();

    System.out.println("Concatenation = " + ((end - start)) + " millisecond");

    start = System.currentTimeMillis();

    for (int i = 0; i < 1000000; i++) {
        StringBuilder bldString = new StringBuilder("Hi ");
        bldString.append(i).append("Hi to you ").append(i * 2).toString();
    }

    end = System.currentTimeMillis();

    System.out.println("String Builder = " + ((end - start)) + " millisecond");

  }
}

Shell Commands : (compile and run StringTest 5 times)

> javac StringTest.java
> sh -c "for i in \$(seq 1 5); do echo \"Run \${i}\"; java StringTest; done"

Results :

Run 1
Format = 1290 millisecond
Concatenation = 115 millisecond
String Builder = 130 millisecond

Run 2
Format = 1265 millisecond
Concatenation = 114 millisecond
String Builder = 126 millisecond

Run 3
Format = 1303 millisecond
Concatenation = 114 millisecond
String Builder = 127 millisecond

Run 4
Format = 1297 millisecond
Concatenation = 114 millisecond
String Builder = 127 millisecond

Run 5
Format = 1270 millisecond
Concatenation = 114 millisecond
String Builder = 126 millisecond
Renny answered 3/12, 2013 at 21:29 Comment(0)
C
9

String.format() is more than just concatenating strings. For example, you can display numbers in a specific locale using String.format().

However, if you don't care about localisation, there is no functional difference. Maybe the concatenation is faster than the other but, in most cases, it will be negligible.

Cordula answered 29/5, 2009 at 11:1 Comment(0)
T
6

Generally, string concatenation should be prefered over String.format. The latter has two main disadvantages:

  1. It does not encode the string to be built in a local manner.
  2. The building process is encoded in a string.

By point 1, I mean that it is not possible to understand what a String.format() call is doing in a single sequential pass. One is forced to go back and forth between the format string and the arguments, while counting the position of the arguments. For short concatenations, this is not much of an issue. In these cases however, string concatenation is less verbose.

By point 2, I mean that the important part of the building process is encoded in the format string (using a DSL). Using strings to represent code has many disadvantages. It is not inherently type-safe, and complicates syntax-highlighting, code analysis, optimization, etc.

Of course, when using tools or frameworks external to the Java language, new factors can come into play.

Teerell answered 2/1, 2016 at 13:41 Comment(0)
B
3

Wrong test repeated many times You should use {} no %s .

public static void main(String[] args) throws Exception {
long start = System.currentTimeMillis();
for (int i = 0; i < 1000000; i++) {
  String s = "Hi " + i + "; Hi to you " + i * 2;
}
long end = System.currentTimeMillis();
System.out.println("Concatenation = " + ((end - start)) + " millisecond");

start = System.currentTimeMillis();
for (int i = 0; i < 1000000; i++) {
  String s = String.format("Hi %s; Hi to you %s", i, +i * 2);
}
end = System.currentTimeMillis();
System.out.println("Wrong use of the message format  = " + ((end - start)) + " millisecond");

start = System.currentTimeMillis();
for (int i = 0; i < 1000000; i++) {
  String s = String.format("Hi {0}; Hi to you {1}", i, +i * 2);
}
end = System.currentTimeMillis();
System.out.println("Good use of the message format = " + ((end - start)) + " millisecond");

}

Concatenation = 88 millisecond
Wrong use of the message format  = 1075 millisecond 
Good use of the message format = 376 millisecond
Belita answered 8/4, 2022 at 11:32 Comment(0)
F
2

I haven't done any specific benchmarks, but I would think that concatenation may be faster. String.format() creates a new Formatter which, in turn, creates a new StringBuilder (with a size of only 16 chars). That's a fair amount of overhead especially if you are formatting a longer string and StringBuilder keeps having to resize.

However, concatenation is less useful and harder to read. As always, it's worth doing a benchmark on your code to see which is better. The differences may be negligible in server app after your resource bundles, locales, etc are loaded in memory and the code is JITted.

Maybe as a best practice, it would be a good idea to create your own Formatter with a properly sized StringBuilder (Appendable) and Locale and use that if you have a lot of formatting to do.

Fontanel answered 29/5, 2009 at 14:55 Comment(0)
D
2

There could be a perceptible difference.

String.format is quite complex and uses a regular expression underneath, so don't make it a habit to use it everywhere, but only where you need it.

StringBuilder would be an order of magnitude faster (as someone here already pointed out).

Denicedenie answered 4/10, 2011 at 23:58 Comment(0)
T
1

I think we can go with MessageFormat.format as it should be good at both readability and also performance aspects.

I used the same program which one used by Icaro in his above answer and I enhanced it with appending code for using MessageFormat to explain the performance numbers.

  public static void main(String[] args) {
    long start = System.currentTimeMillis();
    for (int i = 0; i < 1000000; i++) {
      String s = "Hi " + i + "; Hi to you " + i * 2;
    }
    long end = System.currentTimeMillis();
    System.out.println("Concatenation = " + ((end - start)) + " millisecond");

    start = System.currentTimeMillis();
    for (int i = 0; i < 1000000; i++) {
      String s = String.format("Hi %s; Hi to you %s", i, +i * 2);
    }
    end = System.currentTimeMillis();
    System.out.println("Format = " + ((end - start)) + " millisecond");

    start = System.currentTimeMillis();
    for (int i = 0; i < 1000000; i++) {
      String s = MessageFormat.format("Hi %s; Hi to you %s", i, +i * 2);
    }
    end = System.currentTimeMillis();
    System.out.println("MessageFormat = " + ((end - start)) + " millisecond");
  }

Concatenation = 69 millisecond

Format = 1435 millisecond

MessageFormat = 200 millisecond

UPDATES:

As per SonarLint Report, Printf-style format strings should be used correctly (squid:S3457)

Because printf-style format strings are interpreted at runtime, rather than validated by the compiler, they can contain errors that result in the wrong strings being created. This rule statically validates the correlation of printf-style format strings to their arguments when calling the format(...) methods of java.util.Formatter, java.lang.String, java.io.PrintStream, MessageFormat, and java.io.PrintWriter classes and the printf(...) methods of java.io.PrintStream or java.io.PrintWriter classes.

I replace the printf-style with the curly-brackets and I got something interesting results as below.

Concatenation = 69 millisecond
Format = 1107 millisecond
Format:curly-brackets = 416 millisecond
MessageFormat = 215 millisecond
MessageFormat:curly-brackets = 2517 millisecond

My Conclusion:
As I highlighted above, using String.format with curly-brackets should be a good choice to get benefits of good readability and also performance.

Tempera answered 30/5, 2019 at 11:9 Comment(1)
This is not a way you should write any test because the result is unpredictable. There are tons of rules you broke with this code i.e. you're not warming it up, you ignore Java optimizer etc.Incrust
L
0

It takes a little time to get used to String.Format, but it's worth it in most cases. In the world of NRA (never repeat anything) it's extremely useful to keep your tokenized messages (logging or user) in a Constant library (I prefer what amounts to a static class) and call them as necessary with String.Format regardless of whether you are localizing or not. Trying to use such a library with a concatenation method is harder to read, troubleshoot, proofread, and manage with any any approach that requires concatenation. Replacement is an option, but I doubt it's performant. After years of use, my biggest problem with String.Format is the length of the call is inconveniently long when I'm passing it into another function (like Msg), but that's easy to get around with a custom function to serve as an alias.

Laocoon answered 10/5, 2019 at 15:56 Comment(0)
R
0

Format = 1508 millisecond Concatenation = 31 millisecond

Process finished with exit code 0

Concat is much better than format.

Rodas answered 21/7, 2023 at 15:10 Comment(1)
As it’s currently written, your answer is unclear. Please edit to add additional details that will help others understand how this addresses the question asked. You can find more information on how to write good answers in the help center.Contrariety
B
-1

You cannot compare String Concatenation and String.Format by the program above.

You may try this also be interchanging the position of using your String.Format and Concatenation in your code block like the below

public static void main(String[] args) throws Exception {      
  long start = System.currentTimeMillis();

  for( int i=0;i<1000000; i++){
    String s = String.format( "Hi %s; Hi to you %s",i, + i*2);
  }

  long end = System.currentTimeMillis();
  System.out.println("Format = " + ((end - start)) + " millisecond");
  start = System.currentTimeMillis();

  for( int i=0;i<1000000; i++){
    String s = "Hi " + i + "; Hi to you " + i*2;
  }

  end = System.currentTimeMillis();
  System.out.println("Concatenation = " + ((end - start)) + " millisecond") ;
}

You will be surprised to see that Format works faster here. This is since the intial objects created might not be released and there can be an issue with memory allocation and thereby the performance.

Begin answered 28/9, 2011 at 4:44 Comment(2)
have you tried your code? Concatenation is always ten time fasterContemptible
what about the millis taken to execute this "System.currentTimeMillis()" :P.Telesthesia

© 2022 - 2024 — McMap. All rights reserved.