354

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.

1

16 Answers 16

289

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)
2
  • 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.
    – markvgti
    Commented Aug 20, 2011 at 7:28
  • 16
    Better late than never, random Java version: docs.oracle.com/javase/1.5.0/docs/api/java/util/…
    – Aksel
    Commented Jan 17, 2012 at 19:53
249

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.

8
  • 23
    They are all bad practice. Use StringBuilder. Commented Aug 11, 2011 at 16:37
  • 18
    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?
    – Icaro
    Commented Jan 2, 2012 at 10:46
  • 153
    @AmirRaminar: The compiler converts "+" to calls to StringBuilder automatically. Commented Feb 23, 2012 at 14:50
  • 58
    @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.
    – ccpizza
    Commented Nov 21, 2014 at 9:57
  • 7
    @Joffrey: what I meant was that for loops + does not get converted to StringBuilder.append() but instead a new StringBuilder() happens on each iteration.
    – ccpizza
    Commented Mar 20, 2017 at 10:49
62

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.

2
  • 31
    just for the record, modern IDEs (e.g. IntelliJ) assist in arguments count and type matching
    – Ron Klein
    Commented Apr 16, 2015 at 12:41
  • 3
    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 IDE Commented Aug 18, 2016 at 9:03
46

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
11
  • 29
    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. Commented Oct 31, 2012 at 2:33
  • 18
    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? Commented Nov 21, 2013 at 2:47
  • 5
    Jamey Sharp is exactly right. Invoking bldString.toString() is about the same if not slower than string concatenation.
    – Akos Cz
    Commented Dec 3, 2013 at 20:18
  • 4
    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)
    – TechTrip
    Commented Apr 1, 2014 at 23:43
  • 10
    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.
    – alobodzk
    Commented May 15, 2017 at 12:25
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.

4
  • 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.
    – Jonik
    Commented May 29, 2009 at 11:01
  • 3
    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.
    – workmad3
    Commented May 29, 2009 at 11:07
  • 9
    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).
    – Jonik
    Commented May 29, 2009 at 11:13
  • 2
    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). Commented May 30, 2017 at 23:40
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
2
  • 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/… Commented Dec 13, 2016 at 16:09
  • 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!
    – mohamnag
    Commented Sep 21, 2018 at 9:24
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
10

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.

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.

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
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.

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).

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.

1
  • 3
    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.
    – horvoje
    Commented Jul 16, 2020 at 13:53
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.

0

Format = 1508 millisecond Concatenation = 31 millisecond

Process finished with exit code 0

Concat is much better than format.

1
  • 2
    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.
    – Community Bot
    Commented Jul 25, 2023 at 12:12
-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.

2
  • 3
    have you tried your code? Concatenation is always ten time faster
    – Icaro
    Commented Nov 9, 2011 at 17:59
  • what about the millis taken to execute this "System.currentTimeMillis()" :P. Commented Mar 8, 2018 at 10:12

Not the answer you're looking for? Browse other questions tagged or ask your own question.