Java String Interpolation Performance Study
2021-04-10tl;dr In Java,
String.replace
should be used for string interpolation to reduce performance impact, and concatenating string with+
is performant as well.
Generally, to perform string interpolation, one would provide a templated string, e.g. "[{1}] message received with id: {2} with size: {3}"
. In Java, the closest you get out of the box is String.format
for this.
However, there is a common belief that String.format
is slow and should not be used in performance critical applications. Some suggests to use String.replace
instead in such situation.
Benchmarking
To test which method is better, I wrote code to interpolate string for integer, string and double in both ways and do benchmark with JMH on a Mac with 1.6 GHz Intel Core i5 with Zulu OpenJDK 64-Bit Server VM (build 11.0.10+9-LTS, mixed mode) based on throughput.
Interpolation with replace
@Benchmark
public void testStringReplace(){
System.out.println(
"{1} {2} {3}"
.replace("{1}", String.valueOf(11))
.replace("{2}", "Eleven")
.replace("{3}", String.valueOf(1.1))
);
}
Interpolation with format
@Benchmark
public void testStringFormat(){
System.out.println(String.format("%d %s %f", 11, "Eleven", 1.1));
}
Result
Benchmark Mode Cnt Score Error Units
StringInterpolationJavaBenchmark.testStringFormat thrpt 2 25191.009 ops/s
StringInterpolationJavaBenchmark.testStringReplace thrpt 2 46889.833 ops/s
As the result suggest, using replace
is much faster than format
Why
Comparing their implementation tells most of the story. The following analysis is based on source code of OpenJDK 11
Implementation of String.replace
public String replace(CharSequence target, CharSequence replacement) {
String tgtStr = target.toString();
String replStr = replacement.toString();
int j = indexOf(tgtStr);
if (j < 0) {
return this;
}
int tgtLen = tgtStr.length();
int tgtLen1 = Math.max(tgtLen, 1);
int thisLen = length();
int newLenHint = thisLen - tgtLen + replStr.length();
if (newLenHint < 0) {
throw new OutOfMemoryError();
}
StringBuilder sb = new StringBuilder(newLenHint);
int i = 0;
do {
sb.append(this, i, j).append(replStr);
i = j + tgtLen;
} while (j < thisLen && (j = indexOf(tgtStr, j + tgtLen1)) > 0);
return sb.append(this, i, thisLen).toString();
}
It loop over the original string, append the either the original section of string, or the replaced string to a new string builder, and return the newly built string from the builder.
Implementation of String.format
public static String format(String format, Object... args) {
return new Formatter().format(format, args).toString();
}
Every time it will create a new Formatter
.
private List<FormatString> parse(String s) {
ArrayList<FormatString> al = new ArrayList<>();
Matcher m = fsPattern.matcher(s);
// other code...
The Formatter
requires to parse the format with RegEx on each invocation. Here, RegEx parsing is expensive in terms of performance.
Thus, String.replace
is much performant than String.format
.
Can we go further?
replaceFirst
Some may think replaceFirst
can reduce iterations by early exit in finding string.
@Benchmark
public void testStringReplaceFirst(){
// NOTE: it is regex "\\{}", not simple curly bracket "{}"
System.out.println(
"{} {} {}"
.replaceFirst("\\{}", String.valueOf(11))
.replaceFirst("\\{}", "Eleven")
.replaceFirst("\\{}", String.valueOf(1.1))
);
}
Benchmark Mode Cnt Score Error Units
StringInterpolationJavaBenchmark.testStringReplaceFirst thrpt 2 30718.865 ops/s
Here, the performance is worsen, because replaceFirst
relies on RegEx.
Concatenate strings with +
@Benchmark
public void testStringConcat(){
System.out.println(
String.valueOf(11) + " " + "Eleven" + " " + String.valueOf(1.1)
);
}
Benchmark Mode Cnt Score Error Units
StringInterpolationJavaBenchmark.testStringConcat thrpt 2 47188.903 ops/s
The performance is better than replace
here, but it doesn't look like string interpolation and the code is not as elegant as the the replace
version.
Closing
If your Java application is performance critical, String.replace
and concatenate strings with +
are more performant than String.format
.
All above benchmark code is available in this repo.
If you like this article, remember to show your support by buy me a book.