java - 如果找不到匹配项,替换会做什么? (在引擎盖下)

标签 java regex string performance replace

我有很长的字符串,如果它出现,需要删除一个模式。但它出现在字符串中的情况非常罕见。

如果我这样做:

str = str.replace("pattern", "");

然后看起来我正在创建一个新字符串(因为 Java 字符串是不可变的),如果原始字符串没问题,这将是一种浪费。我应该先检查匹配项,然后仅在找到匹配项时才进行替换吗?

最佳答案

简答

查看各种实现的文档,似乎没有一个需要 String.replace(CharSequence, CharSequence) 方法在找不到匹配项时返回相同的字符串。

如果没有文档的要求,在找不到匹配项的情况下,实现可能会或可能不会优化方法。最好像没有优化一样编写代码,以使确保它在 JRE 的任何实现或版本上都能正确运行。

特别是,当未找到匹配项时,Oracle 的实现(版本 8-b123)返回相同的字符串对象,而 GNU 类路径(版本 0.95)无论如何都会返回一个新的字符串对象。

如果您在任何文档中找到任何子句要求 String.replace(CharSequence, CharSequence) 返回相同的 String 对象没有找到匹配项,请发表评论。

长答案

The long answer below is to show that different implementation may or may not optimize the case where no match is found.

让我们看看Oracle对String.replace(CharSequence, CharSequence)方法的实现和GNU Classpath的实现。

GNU 类路径

Note: This is correct as of the time of writing. While the link is not likely to change in the future, the content of the link is likely to change to a newer version of GNU Classpath and may go out of sync with the quoted content below. If the change affects the correctness, please leave a comment.

让我们看看 GNU Classpath 对 String.replace(CharSequence, CharSequence) 的实现(引用 0.95 版)。

public String replace (CharSequence target, CharSequence replacement)
{
    String targetString = target.toString();
    String replaceString = replacement.toString();
    int targetLength = target.length();
    int replaceLength = replacement.length();

    int startPos = this.indexOf(targetString);
    StringBuilder result = new StringBuilder(this);    
    while (startPos != -1)
    {
        // Replace the target with the replacement
        result.replace(startPos, startPos + targetLength, replaceString);

        // Search for a new occurrence of the target
        startPos = result.indexOf(targetString, startPos + replaceLength);
    }
    return result.toString();
}

让我们检查StringBuilder.toString()的源代码.由于这决定了返回值,如果 StringBuilder.toString() 复制缓冲区,那么我们不需要进一步检查上面的任何代码。

/**
 * Convert this <code>StringBuilder</code> to a <code>String</code>. The
 * String is composed of the characters currently in this StringBuilder. Note
 * that the result is a copy, and that future modifications to this buffer
 * do not affect the String.
 *
 * @return the characters in this StringBuilder
 */

public String toString()
{
    return new String(this);
}

如果文档无法说服您,只需遵循 String 构造函数即可。最终,非公共(public)构造函数 String(char[], int, int, boolean)被调用,boolean dont_copy 设置为 false,这意味着新的 String 必须复制缓冲区。

 589:   public String(StringBuilder buffer)
 590:   {
 591:       this(buffer.value, 0, buffer.count);
 592:   }

 245:   public String(char[] data, int offset, int count)
 246:   {
 247:       this(data, offset, count, false);
 248:   }

 594:   /**
 595:    * Special constructor which can share an array when safe to do so.
 596:    *
 597:    * @param data the characters to copy
 598:    * @param offset the location to start from
 599:    * @param count the number of characters to use
 600:    * @param dont_copy true if the array is trusted, and need not be copied
 601:    * @throws NullPointerException if chars is null
 602:    * @throws StringIndexOutOfBoundsException if bounds check fails
 603:    */
 604:   String(char[] data, int offset, int count, boolean dont_copy)
 605:   {
 606:       if (offset < 0)
 607:           throw new StringIndexOutOfBoundsException("offset: " + offset);
 608:       if (count < 0)
 609:           throw new StringIndexOutOfBoundsException("count: " + count);
 610:       // equivalent to: offset + count < 0 || offset + count > data.length
 611:       if (data.length - offset < count)
 612:           throw new StringIndexOutOfBoundsException("offset + count: "
 613:                                                   + (offset + count));
 614:       if (dont_copy)
 615:       {
 616:           value = data;
 617:           this.offset = offset;
 618:       }
 619:       else
 620:       {
 621:           value = new char[count];
 622:           VMSystem.arraycopy(data, offset, value, 0, count);
 623:           this.offset = 0;
 624:       }
 625:       this.count = count;
 626:   }

这些证据表明 GNU Classpath 的 String.replace(CharSequence, CharSequence) 实现不会返回相同的字符串。

甲骨文

在 Oracle 的实现中 String.replace(CharSequence, CharSequence) (引用版本 8-b123),该方法使用 Pattern 类进行替换。

public String replace(CharSequence target, CharSequence replacement) {
    return Pattern.compile(target.toString(), Pattern.LITERAL).matcher(
            this).replaceAll(Matcher.quoteReplacement(replacement.toString()));
}

Matcher.replaceAll(String)CharSequence 上调用 toString() 函数,并在找不到匹配项时返回它:

public String replaceAll(String replacement) {
    reset();
    boolean result = find();
    if (result) {
        StringBuffer sb = new StringBuffer();
        do {
            appendReplacement(sb, replacement);
            result = find();
        } while (result);
        appendTail(sb);
        return sb.toString();
    }
    return text.toString();
}

String 实现了 CharSequence接口(interface),并且由于字符串将自身传递给 Matcher,让我们看一下 String.toString :

public String toString() {
    return this;
}

由此,我们可以得出结论,Oracle 的实现在未找到匹配项时返回相同的字符串。

关于java - 如果找不到匹配项,替换会做什么? (在引擎盖下),我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/26750963/

相关文章:

java - 在 spring 中访问缓存的 ApplicationContext

java - com.bea.wli.config.deployment.server.ServerDeploymentConflictException:由于与正在进行的其他工作发生冲突,无法激活 session

php - 数据库表中的正则表达式搜索和替换

Python 正则表达式字符串提取

c - 相同的字符不同的数字

c - C语言中将字符串赋值给指针

java - JComponent 的裁剪矩形是如何计算的?

java - Springs MockMvc 以两种略有不同的方法之一返回空内容

javascript - 在程序中进行多层数据验证是否可以深度防御故障或代码困惑的偏执狂?

r - 列名称的 grep 模式