仕事で少し迷ったので調べてみた。
環境
- OS ・・・ Mac OS X
- CPU ・・・ 1.7 GHz Intel Core i7
- Java ・・・ jdk 8u73
コード
public class Test {
static final String TEXT = "prefix_ABCDEFG";
static final String PREFIX = "prefix_";
static final String REGEXP_PREFIX = "^prefix_";
static final String EMPTY = "";
public static void main(String[] args) {
IntStream.rangeClosed(0, 10).forEach(i -> {
exec("warm up", Test::test_replace);
exec("replace + startsWith", Test::test_replace);
exec("replaceAll", Test::test_replaceAll);
exec("replaceFirst", Test::test_replaceFirst);
exec("substring + startsWith", Test::test_substringIfStartsWith);
});
}
static void exec(String test, Consumer<String> cons) {
//ウォームアップ
IntStream.of(20).forEach(i -> cons.accept(TEXT));
long start = System.nanoTime();
IntStream.rangeClosed(0, 1_000_000).forEach(i -> cons.accept(TEXT));
long time = System.nanoTime() - start;
System.out.println(String.format("%s : %d ms",
test, time /1000 ));
}
static void test_replace(String text) {
if(!text.startsWith(PREFIX)) return;
text.replace(PREFIX, EMPTY);
}
static void test_replaceAll(String text) {
text.replaceAll(REGEXP_PREFIX, EMPTY);
}
static void test_replaceFirst(String text) {
text.replaceFirst(REGEXP_PREFIX, EMPTY);
}
static void test_substringIfStartsWith(String text) {
if(!text.startsWith(PREFIX)) return;
text.substring(PREFIX.length(), TEXT.length());
}
}
結果
パターン | 速度(平均) |
---|---|
replace + startsWith | 367775 ms |
replaceAll | 353670 ms |
replaceFirst | 346915 ms |
substring + startsWith | 16816 ms |
まあ、replaceは正規表現つかってますからね。。
ということでパフォーマンスが求められる箇所はsubstringですね。
追記
@kazuki43zoo さんにコメントでご指摘いただいたので。
String.replaceが遅い原因として、実行するごとに毎回正規表現をコンパイルしていることが挙げられます。
したがって、正規表現をあらかじめコンパイルして使い回せばより速くなることが期待できます。
上記のコードに次のメソッドを足します。
static Pattern pattern = Pattern.compile(REGEXP_PREFIX);
static void test_pattern_replaceAll(String text) {
pattern.matcher(text).replaceAll(EMPTY);
}
static void test_pattern_replaceFirst(String text) {
pattern.matcher(text).replaceFirst(text);
}
結果
パターン | 速度(平均) |
---|---|
replace + startsWith | 367898 ms |
replaceAll | 362225 ms |
replaceFirst | 334916 ms |
substring + startsWith | 16061 ms |
Pattern.replaceAll | 179644 ms |
Pattern.replaceFirst | 237790 ms |
とまあreplaceAllについては約2倍の実行速度になりました。
replaceFirstも2倍とまではいきませんが、確実に速くなっています。
ですが、やっぱりsubstringが最速なのは同じですね^^;