The String.replace() method you linked to takes two char values, so it only ever replaces on character with another (possibly multiple times, 'though).
The StringUtils.replace() method on the other hand takes String values as the search string and replacement, so it can replace longer substrings.
The comparable method in Java would be replaceAll(). replaceAll() is likely to be slower than the StringUtils method, because it supports regular expressions and thus introduces the overhead of compiling the search string first and running a regex search.
Note that Java 5 introduced String.replace(CharSequence, CharSequence) which does the same thing as StringUtils.replace(String,String) (except that it throws a NullPointerException if any of its arguments are null). Note that CharSequence is an interface implemented by String, so you can use plain old String objects here.
The String.replace() method you linked to takes two char values, so it only ever replaces on character with another (possibly multiple times, 'though).
The StringUtils.replace() method on the other hand takes String values as the search string and replacement, so it can replace longer substrings.
The comparable method in Java would be replaceAll(). replaceAll() is likely to be slower than the StringUtils method, because it supports regular expressions and thus introduces the overhead of compiling the search string first and running a regex search.
Note that Java 5 introduced String.replace(CharSequence, CharSequence) which does the same thing as StringUtils.replace(String,String) (except that it throws a NullPointerException if any of its arguments are null). Note that CharSequence is an interface implemented by String, so you can use plain old String objects here.
public class Compare {
public static void main(String[] args) {
StringUtils.isAlphanumeric(""); // Overhead of static class initialization for StringUtils
String key = "0 abcdefghijklmno" + Character.toString('\n') + Character.toString('\r');
String key1 = replace1(key);
String key2 = replace2(key);
}
private static String replace1(String key) {
long start = System.nanoTime();
key = StringUtils.replaceChars(key, ' ', '_');
key = StringUtils.replaceChars(key, '\n', '_');
key = StringUtils.replaceChars(key, '\r', '_');
long end = System.nanoTime() - start;
System.out.println("Time taken : " + end);
return key;
}
public static String replace2(String word) {
long start = System.nanoTime();
char[] charArr = word.toCharArray();
int length = charArr.length;
for (int i = 0; i < length; i++) {
if (charArr[i] == ' ' || charArr[i] == '\n' || charArr[i] == '\r') {
charArr[i] = '_';
}
}
String temp = new String(charArr);
long end = System.nanoTime() - start;
System.out.println("Time taken : " + end);
return temp;
}
}
Time taken : 6400
Time taken : 5888
Times are almost the same!
I've edited the code to drop out overheads of replace2 which were not because of JDK implementation.
In modern Java, this is not the case anymore. String.replace was improved in Java-9 moving from regular expression to StringBuilder, and was improved even more in Java-13 moving to direct allocation of the target byte[] array calculating its exact size in advance. Thanks to internal JDK features used, like the ability to allocate an uninitialized array, ability to access String coder and ability to use private String constructor which avoids copying, it's unlikely that current implementation can be beaten by a third-party implementation.
Here are my benchmarking results for your test using JDK 8, JDK 9 and JDK 13 (caliper:0.5-rc1; commons-lang3:3.9)
Java 8 (4x slower indeed):
0% Scenario{vm=java, trial=0, benchmark=M1} 291.42 ns; σ=6.56 ns @ 10 trials
50% Scenario{vm=java, trial=0, benchmark=M2} 70.34 ns; σ=0.15 ns @ 3 trials
benchmark ns linear runtime
M1 291.4 ==============================
M2 70.3 =======
Java 9 (almost equal performance):
0% Scenario{vm=java, trial=0, benchmark=M2} 99,15 ns; σ=8,34 ns @ 10 trials
50% Scenario{vm=java, trial=0, benchmark=M1} 103,43 ns; σ=9,01 ns @ 10 trials
benchmark ns linear runtime
M2 99,1 ============================
M1 103,4 ==============================
Java 13 (standard method is 38% faster):
0% Scenario{vm=java, trial=0, benchmark=M2} 91,64 ns; σ=5,12 ns @ 10 trials
50% Scenario{vm=java, trial=0, benchmark=M1} 57,38 ns; σ=2,51 ns @ 10 trials
benchmark ns linear runtime
M2 91,6 ==============================
M1 57,4 ==================
From the source code of java.lang.String1:
public String replace(CharSequence target, CharSequence replacement) {
return Pattern
.compile(target.toString(), Pattern.LITERAL)
.matcher(this )
.replaceAll(
Matcher.quoteReplacement(replacement.toString()));
}
String.replace(CharSequence target, CharSequence replacement) is implemented with java.util.regex.Pattern, therefore, it is not surprising that it is slower that StringUtils.replace(String text, String searchString, String replacement)2, which is implemented with indexOf and StringBuffer.
public static String replace(String text, String searchString, String replacement) {
return replace(text, searchString, replacement, -1);
}
public static String replace(String text, String searchString, String replacement, int max) {
if (isEmpty(text) || isEmpty(searchString) || replacement == null || max == 0) {
return text;
}
int start = 0;
int end = text.indexOf(searchString, start);
if (end == -1) {
return text;
}
int replLength = searchString.length();
int increase = replacement.length() - replLength;
increase = (increase < 0 ? 0 : increase);
increase *= (max < 0 ? 16 : (max > 64 ? 64 : max));
StringBuffer buf = new StringBuffer(text.length() + increase);
while (end != -1) {
buf.append(text.substring(start, end)).append(replacement);
start = end + replLength;
if (--max == 0) {
break;
}
end = text.indexOf(searchString, start);
}
buf.append(text.substring(start));
return buf.toString();
}
Footnote
1 The version that I links to and copied source code from is JDK 7
2 The version that I links to and copied source code from is common-lang-2.5