String.replace()和String.replaceAll()性能对比

本文对比了Java中String.replace()和String.replaceAll()方法在不同场景下的性能表现,并通过实验量化了两者之间的性能差异。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

Java中有些常用的API其实值得仔细研究一下,比如String.replace()和String.replaceAll()。以Android7.1源代码为例,仔细研究一下这两个API。定义如下:

    /**
     * Replaces each substring of this string that matches the literal target
     * sequence with the specified literal replacement sequence. The
     * replacement proceeds from the beginning of the string to the end, for
     * example, replacing "aa" with "b" in the string "aaa" will result in
     * "ba" rather than "ab".
     *
     * @param  target The sequence of char values to be replaced
     * @param  replacement The replacement sequence of char values
     * @return  The resulting string
     * @throws NullPointerException if <code>target</code> or
     *         <code>replacement</code> is <code>null</code>.
     * @since 1.5
     */
    public String replace(CharSequence target, CharSequence replacement)
    /**
     * Replaces each substring of this string that matches the given <a
     * href="../util/regex/Pattern.html#sum">regular expression</a> with the
     * given replacement.
     *
     * <p> An invocation of this method of the form
     * <i>str</i><tt>.replaceAll(</tt><i>regex</i><tt>,</tt> <i>repl</i><tt>)</tt>
     * yields exactly the same result as the expression
     *
     * <blockquote><tt>
     * {@link java.util.regex.Pattern}.{@link java.util.regex.Pattern#compile
     * compile}(</tt><i>regex</i><tt>).{@link
     * java.util.regex.Pattern#matcher(java.lang.CharSequence)
     * matcher}(</tt><i>str</i><tt>).{@link java.util.regex.Matcher#replaceAll
     * replaceAll}(</tt><i>repl</i><tt>)</tt></blockquote>
     *
     *<p>
     * Note that backslashes (<tt>\</tt>) and dollar signs (<tt>$</tt>) in the
     * replacement string may cause the results to be different than if it were
     * being treated as a literal replacement string; see
     * {@link java.util.regex.Matcher#replaceAll Matcher.replaceAll}.
     * Use {@link java.util.regex.Matcher#quoteReplacement} to suppress the special
     * meaning of these characters, if desired.
     *
     * @param   regex
     *          the regular expression to which this string is to be matched
     * @param   replacement
     *          the string to be substituted for each match
     *
     * @return  The resulting <tt>String</tt>
     *
     * @throws  PatternSyntaxException
     *          if the regular expression's syntax is invalid
     *
     * @see java.util.regex.Pattern
     *
     * @since 1.4
     * @spec JSR-51
     */
    public String replaceAll(String regex, String replacement) 

可以看到,
(1)replace():返回输入字符串的一个副本,该副本将字符串中所有出现的target子字符串都替换成replacement。
(2)replaceAll():返回输入字符串的一个副本,改副本将字符串中所有出现的满足正则表达式regex的子字符串都替换成replacement。
显然,两个API的使用场景不同。replaceAll()的功能更强大一些。同时,因为replaceAll()需要处理正则表达式,性能上应该会弱于replace()。但对于同样的需求,性能上有多大的差别的?用一个简单的例子来试验:

        long now = System.currentTimeMillis();
        for (int i = 0 ; i < 1000000 ; i++) {
            "aabbbc".replace("b", "a");
        }
        Log.i("TEST", "replace() : " + (System.currentTimeMillis() - now));

        now = System.currentTimeMillis();
        for (int i = 0 ; i < 1000000 ; i++) {
            "aabbbc".replaceAll("b", "a");
        }
        Log.i("TEST", "replaceAll() : " + (System.currentTimeMillis() - now));

结果:

10-20 16:26:08.401 19518 19670 I TEST    : replace() : 2170
10-20 16:27:47.828 19518 19670 I TEST    : replaceAll() : 99427

可以看到,在将规模放大到100万量级,replaceAll()耗时是replace()的接近50倍。(这里暂时不考虑系统线程调度,仅以开始、结束的系统时间戳作为计时依据。另外,上述例子仅仅是为了对于,实际使用中如果是长度为1的字符串的替换,更合适的API当然是replace(char,char)。)
当然,对于固定字符串的替换,一般情况下都会使用replace();对于复杂的正则表达式,也不能不用replaceAll。两者的交叉点往往在于简单的组合,譬如
replace(“a”,”1”).replace(“b”,”1”) vs replaceAll(“[ab]”,”1”)
先不考虑代码整洁与否,只关注性能。从前面的50倍差距来看,直观感觉,是否50是一个临界点呢?也就是说,使用replace()需要调用50次,而replaceAll()实际上需要调用1次。临界点是否存在?继续用试验来探讨。
先用这样一段代码来试探:

                long now = System.currentTimeMillis();
                for (int i = 0 ; i < 100000 ; i++) {
                    "abcdefghijklmnopqrstuvwxyz"
                            .replace("a", "1")
                            .replace("b", "1")
                            .replace("c", "1")
                            .replace("d", "1")
                            .replace("e", "1")
                            .replace("f", "1")
                            .replace("g", "1")
                            .replace("h", "1")
                            .replace("i", "1")
                            .replace("j", "1");
                }
                Log.i("TEST", "replace() : " + (System.currentTimeMillis() - now));

                now = System.currentTimeMillis();
                for (int i = 0 ; i < 100000 ; i++) {
                    "abcdefghijklmnopqrstuvwxyz".replaceAll("[a-j]", "1");
                }
                Log.i("TEST", "replaceAll() : " + (System.currentTimeMillis() - now));

规模10万,测试临界点10,结果:

10-20 17:04:55.656 24206 24326 I TEST    : replace() : 3274
10-20 17:05:13.844 24206 24326 I TEST    : replaceAll() : 18188

依然有约6倍的耗时,具体到单个replace()就是60倍的耗时。
好,临界点扩大到26(满26个英文字母):

                long now = System.currentTimeMillis();
                for (int i = 0 ; i < 100000 ; i++) {
                    "abcdefghijklmnopqrstuvwxyz"
                            .replace("a", "1")
                            .replace("b", "1")
                            .replace("c", "1")
                            .replace("d", "1")
                            .replace("e", "1")
                            .replace("f", "1")
                            .replace("g", "1")
                            .replace("h", "1")
                            .replace("i", "1")
                            .replace("j", "1")
                            .replace("k", "1")
                            .replace("l", "1")
                            .replace("m", "1")
                            .replace("n", "1")
                            .replace("o", "1")
                            .replace("p", "1")
                            .replace("q", "1")
                            .replace("r", "1")
                            .replace("s", "1")
                            .replace("t", "1")
                            .replace("u", "1")
                            .replace("v", "1")
                            .replace("w", "1")
                            .replace("x", "1")
                            .replace("y", "1")
                            .replace("z", "1");
                }
                Log.i("TEST", "replace() : " + (System.currentTimeMillis() - now));

                now = System.currentTimeMillis();
                for (int i = 0 ; i < 100000 ; i++) {
                    "abcdefghijklmnopqrstuvwxyz".replaceAll("[a-z]", "1");
                }
                Log.i("TEST", "replaceAll() : " + (System.currentTimeMillis() - now));

结果:

10-20 17:02:50.440 22178 22248 I TEST    : replace() : 8232
10-20 17:03:21.954 22178 22248 I TEST    : replaceAll() : 31514

仍有接近4倍的耗时,具体到单个replace()就是100倍的耗时。
随着正则表达式本身的膨胀,replaceAll()的耗时也在增加。
以上已经将26个字母都涵盖到,依然没有达到临界点,所以基本上可以得到结论,对于简单型的替换而言,单以性能考虑,显然replace()是更好的选择。

Java 中,`String.replaceAll()` `StringUtils.replaceAll()` 是两个用于字符串替换的方法,但它们之间存在显著的差异,包括底层实现、功能扩展以及使用场景。 ### 三、方法来源与功能对比 `String.replaceAll()` 是 Java 原生 `String` 类的一个方法,它基于正则表达式进行匹配替换。其语法为: ```java public String replaceAll(String regex, String replacement) ``` 该方法将字符串中所有与正则表达式 `regex` 匹配的部分替换为指定的替换字符串 `replacement` [^2]。 相比之下,`StringUtils.replaceAll()` 来自 Apache Commons Lang 库中的 `StringUtils` 工具类。虽然它的作用与 `String.replaceAll()` 类似,但它提供了更友好的 API 设计更强的空值处理能力。例如,该方法在输入字符串为 `null` 时不会抛出异常,而是直接返回 `null`,从而减少了显式的空值检查需求。 ### 四、正则表达式依赖 `String.replaceAll()` 的一个关键特性是它依赖于正则表达式进行匹配。这意味着如果传入的 `regex` 参数包含特殊字符(如 `.`, `*`, `+`, `?` 等),必须进行转义处理,否则可能导致意外行为或安全漏洞 [^1]。 而 `StringUtils.replaceAll()` 在内部依然调用了 `String.replaceAll()`,因此它同样依赖于正则表达式。然而,Apache Commons 提供了其他非正则版本的方法,如 `StringUtils.replace()`,用于简单的字面量替换,不涉及正则表达式解析,适用于不需要复杂模式匹配的场景。 ### 五、异常处理与健壮性 由于 `String.replaceAll()` 是基于正则表达式的,当传入的 `regex` 参数无效时,会抛出 `PatternSyntaxException` 异常。这要求开发者在使用时必须确保正则表达式的合法性,否则可能引发运行时错误 [^4]。 而 `StringUtils.replaceAll()` 没有从根本上改变这一机制,但由于它是工具类的一部分,通常与其他辅助方法一起使用,开发者可以在调用前对参数进行更全面的校验,提高代码的健壮性。 ### 六、性能考量 从性能角度来看,两者在底层都依赖于相同的正则表达式引擎(Java 的 `Pattern` `Matcher` 类),因此在执行效率上差异不大。但在某些特定场景下,例如仅需替换固定字符串而非正则模式时,应优先使用 `StringUtils.replace()` 或 `String.replace()`,以避免不必要的正则编译开销。 ### 七、使用建议 - 如果需要进行基于正则表达式的全局替换,并且输入数据已知有效,则可以使用 `String.replaceAll()`。 - 如果项目中已经引入了 Apache Commons Lang,并且希望增强代码的可读性健壮性,特别是在处理可能为空的字符串时,推荐使用 `StringUtils.replaceAll()`。 - 对于不需要正则表达式的简单替换操作,应使用 `String.replace(CharSequence target, CharSequence replacement)`,它按字面量进行替换,不会引发正则相关的异常或性能问题 [^1]。 ---
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值