Commons Lang StringUtils.replace性能vs。String.replace

时间:2013-04-26 04:51:18

标签: java

当我比较Apache的StringUtils.replace()String.replace()的表现时,我惊讶地发现前者的速度提高了约4倍。我使用Google的Caliper框架来衡量效果。这是我的测试

public class Performance extends SimpleBenchmark {
    String s = "111222111222";

    public int timeM1(int n) {
        int res = 0;
        for (int x = 0; x < n; x++) {
            res += s.replace("111", "333").length();
        }
        return res;
    }

    public int timeM2(int n) {
        int res = 0;
        for (int x = 0; x < n; x++) {
            res += StringUtils.replace(s, "111", "333", -1).length();
        }
        return res;
    }

    public static void main(String... args) {
        Runner.main(Performance.class, args);
    }
}

输出

 0% Scenario{vm=java, trial=0, benchmark=M1} 9820,93 ns; ?=1053,91 ns @ 10 trials
50% Scenario{vm=java, trial=0, benchmark=M2} 2594,67 ns; ?=58,12 ns @ 10 trials

benchmark   us linear runtime
       M1 9,82 ==============================
       M2 2,59 =======

为什么?两种方法似乎都做同样的工作,StringUtils.replace()更灵活。

5 个答案:

答案 0 :(得分:35)

来自java.lang.String 1 的源代码:

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)是使用java.util.regex.Pattern实现的,因此StringUtils.replace(String text, String searchString, String replacement) 2 的速度慢于{{1} }和indexOf

StringBuffer

脚注

1 我链接到并复制源代码的版本是JDK 7

2 我链接到并复制源代码的版本是common-lang-2.5

答案 1 :(得分:31)

在现代Java中,情况不再如此。 String.replaceJava-9中进行了改进,从正则表达式移至StringBuilder,在Java-13中进行了改进,移至直接分配目标byte[]数组,从而预先计算了其确切大小。由于使用了内部JDK功能,例如分配未初始化的数组的能力,访问String编码器的能力以及使用避免复制的私有String构造函数的能力,因此当前的实现不太可能被第三方实现击败

这是我使用JDK 8,JDK 9和JDK 13进行测试的基准测试结果(caliper:0.5-rc1; commons-lang3:3.9)

Java 8(确实慢了4倍):

 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(几乎相等的性能):

 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(标准方法快38%):

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

答案 2 :(得分:9)

试试这个,你会发现它的性能非常高于Apache的那个:

public static String replace (String source, String os, String ns) {
    if (source == null) {
        return null;
    }
    int i = 0;
    if ((i = source.indexOf(os, i)) >= 0) {
        char[] sourceArray = source.toCharArray();
        char[] nsArray = ns.toCharArray();
        int oLength = os.length();
        StringBuilder buf = new StringBuilder (sourceArray.length);
        buf.append (sourceArray, 0, i).append(nsArray);
        i += oLength;
        int j = i;
        // Replace all remaining instances of oldString with newString.
        while ((i = source.indexOf(os, i)) > 0) {
            buf.append (sourceArray, j, i - j).append(nsArray);
            i += oLength;
            j = i;
        }
        buf.append (sourceArray, j, sourceArray.length - j);
        source = buf.toString();
        buf.setLength (0);
    }
    return source;
}

答案 3 :(得分:6)

我对JMH的测试:https://github.com/qxo/Benchmark4StringReplace 困惑是loukili的方式:

java -jar target/benchmarks.jar StringReplaceBenchmark -wi 3 -i 6 -f 1 -tu ms Benchmark Mode Cnt Score Error Units StringReplaceBenchmark.test4String thrpt 6 1255.017 ± 230.012 ops/ms StringReplaceBenchmark.test4StringUtils thrpt 6 4068.229 ± 67.708 ops/ms StringReplaceBenchmark.test4fast thrpt 6 4821.035 ± 97.790 ops/ms StringReplaceBenchmark.test4lang3StringUtils thrpt 6 3186.007 ± 102.786 ops/ms

答案 4 :(得分:4)

  

为什么?这两种方法似乎都做了同样的工作。

您需要查看源代码并使用分析器进行一些严肃的调查,以获得良好的(技术性)答案。

但是,一种可能的解释是StringUtils.replaceString.replace已针对不同的用例进行了调整。你只看一个案例...用一个非常小的字符串,和一个替换字符串,其大小与被替换的子字符串相同。

另一种可能的解释是Apache开发人员只花了更多时间进行调优。 (并且不要因此而责怪Java开发人员。他们长期以来一直在严格的人员配置限制下工作。在大型计划中,有许多任务比性能调优String.replace更重要。)


事实上,查看源代码,看起来Java 7版本只是使用基于正则表达式的replace。相比之下,Apache版本需要相当长的时间来避免复制。基于该证据,我预计两个版本之间的性能差异对于大型目标字符串来说相对较小。我怀疑Java 7版本在某些情况下可能会更好。

(根据代码中的证据,非技术性解释也是合理的。)