Should I use Java's String.format() if performance is important?
I wrote a small class to test which has the better performance of the two and + comes ahead of format. by a factor of 5 to 6.
Try it your self
import java.io.*;
import java.util.Date;
public class StringTest{
public static void main( String[] args ){
int i = 0;
long prev_time = System.currentTimeMillis();
long time;
for( i = 0; i< 100000; i++){
String s = "Blah" + i + "Blah";
}
time = System.currentTimeMillis() - prev_time;
System.out.println("Time after for loop " + time);
prev_time = System.currentTimeMillis();
for( i = 0; i<100000; i++){
String s = String.format("Blah %d Blah", i);
}
time = System.currentTimeMillis() - prev_time;
System.out.println("Time after for loop " + time);
}
}
Running the above for different N shows that both behave linearly, but String.format
is 5-30 times slower.
The reason is that in the current implementation String.format
first parses the input with regular expressions and then fills in the parameters. Concatenation with plus, on the other hand, gets optimized by javac (not by the JIT) and uses StringBuilder.append
directly.
Performance between String.format and StringBuilder
After doing a little test with StringBuilder
vs String.format
I understood how much time it takes each of them to solve the concatenation. Here the snippet code and the results
Code:
String name = "stackover";
String lName = " flow";
String nick = " stackoverflow";
String email = "stackoverflow@email.com";
int phone = 123123123;
//for (int i = 0; i < 10; i++) {
long initialTime1 = System.currentTimeMillis();
String response = String.format(" - Contact {name=%s, lastName=%s, nickName=%s, email=%s, phone=%d}",
name, lName, nick, email, phone);
long finalTime1 = System.currentTimeMillis();
long totalTime1 = finalTime1 - initialTime1;
System.out.println(totalTime1 + response);
long initialTime2 = System.currentTimeMillis();
final StringBuilder sb = new StringBuilder(" - Contact {");
sb.append("name=").append(name)
.append(", lastName=").append(lName)
.append(", nickName=").append(nick)
.append(", email=").append(email)
.append(", phone=").append(phone)
.append('}');
String response2 = sb.toString();
long finalTime2 = System.currentTimeMillis();
long totalTime2 = finalTime2 - initialTime2;
System.out.println(totalTime2 + response2);
//}
After of run the code several times, I saw that String.format
takes more time:
String.format: 46: Contact {name=stackover, lastName= flow, nickName= stackoverflow, email=stackoverflow@email.com, phone=123123123}
StringBuilder: 0: Contact {name=stackover, lastName= flow, nickName= stackoverflow, email=stackoverflow@email.com, phone=123123123}
String.format: 38: Contact {name=stackover, lastName= flow, nickName= stackoverflow, email=stackoverflow@email.com, phone=123123123}
StringBuilder: 0: Contact {name=stackover, lastName= flow, nickName= stackoverflow, email=stackoverflow@email.com, phone=123123123}
String.format: 51: Contact {name=stackover, lastName= flow, nickName= stackoverflow, email=stackoverflow@email.com, phone=123123123}
StringBuilder: 0: Contact {name=stackover, lastName= flow, nickName= stackoverflow, email=stackoverflow@email.com, phone=123123123}
But if I run the same code inside a loop, the result change.
String.format: 43: Contact {name=stackover, lastName= flow, nickName= stackoverflow, email=stackoverflow@email.com, phone=123123123}
StringBuilder: 0: Contact {name=stackover, lastName= flow, nickName= stackoverflow, email=stackoverflow@email.com, phone=123123123}
String.format: 1: Contact {name=stackover, lastName= flow, nickName= stackoverflow, email=stackoverflow@email.com, phone=123123123}
StringBuilder: 0: Contact {name=stackover, lastName= flow, nickName= stackoverflow, email=stackoverflow@email.com, phone=123123123}
String.format: 1: Contact {name=stackover, lastName= flow, nickName= stackoverflow, email=stackoverflow@email.com, phone=123123123}
StringBuilder: 0: Contact {name=stackover, lastName= flow, nickName= stackoverflow, email=stackoverflow@email.com, phone=123123123}
The first time String.format
runs it takes more time, after of that the time is shorter even though it does not become constant as a result of StringBuilder
As @G.Fiedler said: "String.format
has to parse the format string..."
With these results it can be said that StringBuilder
is more efficient thanString.format
Performance: Java's String.format
I wrote a quick caliper benchmark to compare String.format()
vs. StringBuilder
, StringBuffer
, normal String
+
operator, String.replace()
and String.concat()
methods:
public class StringFormatBenchmark extends SimpleBenchmark {
public void timeStringFormat(int reps) {
while (--reps >= 0) {
String s = String.format("test %d", reps);
}
}
public void timeStringBuilder(int reps) {
while (--reps >= 0) {
String s = new StringBuilder("test ").append(reps).toString();
}
}
public void timeStringBuffer(int reps) {
while (--reps >= 0) {
String s = new StringBuffer("test ").append(reps).toString();
}
}
public void timeStringPlusOperator(int reps) {
while (--reps >= 0) {
String s = "test " + reps;
}
}
public void timeReplace(int reps) {
while (--reps >= 0) {
String s = "test {}".replace("{}", String.valueOf(reps));
}
}
public void timeStringConcat(int reps) {
while (--reps >= 0) {
String s = "test ".concat(String.valueOf(reps));
}
}
public static void main(String[] args) {
new Runner().run(StringFormatBenchmark.class.getName());
}
}
The results follow (Java 1.6.0_26-b03, Ubuntu, 32 bits):
Clearly String.format()
is much slower (by an order of magnitude). Also StringBuffer
is considerably slower than StringBuilder
(as we were taught). Finally StringBuilder
and String
+
operator are almost identical since they compile to very similar bytecode. String.concat()
is a bit slower.
Also don't use String.replace()
if simple concatenation is sufficient.
Does string.format is more efficient than append to string using '+'
Appending using operators is generally more efficient. Format has to take the string and find "%"'s and so, and replace them with corresponding values. Appending is simpler, and shorter to type!
Imagine you are the compiler.
Go through the string to find the %s symbol. Replace it with the value there. Then concatenate.
versus
Concatenate.
Is Java's printf() method efficient for concatenating and printing Strings?
Does Java's printf() use slow concatenation when creating Strings?
Definitely no. The concatenation is fine.
However, it's pretty slow because of the analysis of the format strings. There are quite a few possibilities and a lot of branching and what else.
It would be nearly(*) possible to parse the string upfront and use a List<FormatItem>
where each FormatItem
would take care of one argument (als also add all the preceding fixed text). Once I did something like this and it was 2+ times faster.
(*) It gets more complicated by things like %3$s
.
Difference between formatted string and simple string
There's no difference in terms of the output it produces. The result
variable will have the same contents in each case.
But String.format()
is there so that you can do more powerful formatting, including specifying number of decimal places for floating point numbers, and the like.
I would expect the String.format()
version to be relatively slow, simply because it's more powerful, and has to check the contents of the format string before performing essentially the same operation as you're doing in your second version. In other words, it has to spend some time working out what you want to do, whereas in the second version, the compiler can work it out for itself.
But, really, choosing one of your snippets over the other here for performance reasons looks like the kind of micro-optimisation that will almost never pay off. Unless you have a clear need for performance (i.e., this is running in a tight loop for something performance-critical), you should go for the one that's more readable.
Is it better practice to use String.format over string Concatenation in Java?
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)
String.format() vs + operator
If you are looking for performance only I believe that using StringBuilder/StringBuffer
is the most efficient way to build strings. Even if the Java compiler is smart enough to translate most of String concatenations to StringBuilder
equivalent.
If you are looking for readability the String.format
thing is the much clearer I think, and this is what I use also unless I need to rely on high performance.
So if your main concern is not performance, meaning this code is not in a path that is called a lot, you may prefer to use String.format
as it gives a better idea of the resulting String (like you said).
Besides, using String.format
lets you use the format thing, which means you can use it for padding Strings, formatting numbers, dates, and so on, which would make the code even worse if using simple concatenation.
Edit for Chuu:
Using JAD, you can see that the following code:
public class Test {
public static void main(String[] args) {
String str = "a" + "b" + "c";
String str2 = "foo" + str + "bar" + str;
System.out.println(str2);
}
}
when decompiled will look like:
public class Test {
public static void main(String[] args) {
String str = "abc";
String str2 = new StringBuilder("foo").append(str).append("bar").append(str).toString();
System.out.println(str2);
}
}
Proof of that can also be found using the javap
utility that will show you the Java bytecode under a .class
file:
public static void main(java.lang.String[] args);
0 ldc <String "abc"> [16]
2 astore_1 [str]
3 new java.lang.StringBuilder [18]
6 dup
7 ldc <String "foo"> [20]
9 invokespecial java.lang.StringBuilder(java.lang.String) [22]
12 aload_1 [str]
13 invokevirtual java.lang.StringBuilder.append(java.lang.String) : java.lang.StringBuilder [25]
16 ldc <String "bar"> [29]
18 invokevirtual java.lang.StringBuilder.append(java.lang.String) : java.lang.StringBuilder [25]
21 aload_1 [str]
22 invokevirtual java.lang.StringBuilder.append(java.lang.String) : java.lang.StringBuilder [25]
25 invokevirtual java.lang.StringBuilder.toString() : java.lang.String [31]
28 astore_2 [str2]
29 getstatic java.lang.System.out : java.io.PrintStream [35]
32 aload_2 [str2]
33 invokevirtual java.io.PrintStream.println(java.lang.String) : void [41]
36 return
Related Topics
Jersey 415 Unsupported Media Type
Bytebuffer.Allocate() VS. Bytebuffer.Allocatedirect()
Is a Java Array of Primitives Stored in Stack or Heap
How to Accept Date Params in a Get Request to Spring MVC Controller
Why I Can't Create an Array with Large Size
Creating a "Logical Exclusive Or" Operator in Java
Run Single Test from a Junit Class Using Command-Line
Multipart File Upload Using Spring Rest Template + Spring Web MVC
How to Replace a Character in a String in Java
How to Display Legend for Pie Chart in Columns
Naming Threads and Thread-Pools of Executorservice
How to Redirect to Login Page When Session Is Expired in Java Web Application
How to Add Unicode in Truetype0Font on PDFbox 2.0.0
The Method Getdispatchertype() Is Undefined for the Type Httpservletrequest
Embedded Mongodb When Running Integration Tests
Performance of Traditional for Loop VS Iterator/Foreach in Java