Java中String.replaceFirst(“^ ...”)的预期运行时间是多少

时间:2014-02-04 08:01:40

标签: java performance bytecode

假设我有一个非常长字符串str = "abcdef..."

我想使用"xyz"替换其中str.replaceFirst("^xyz","")的可能前缀。

上述表达式的预期运行时间是多少?

replaceFirst会在第一个字符后立即返回,还是遍及整个字符串?

PS:在编译(或者我应该说'解释')replaceFirst类的String方法时,我需要知道Java字节码解释器如何在场景后工作的人的回答

UPDATE:

无论为了返回结果而创建的中间字符串,请考虑我的问题。在任何情况下都会发生这种情况(无论我使用什么String方法来完成所需的转换)。因此,我删除了引用复杂性的部分,因为它在任何情况下都是O(n) ......

总结一下这个问题:

我可以假设正则表达式匹配本身的运行时间不依赖于str的长度吗?

1 个答案:

答案 0 :(得分:2)

replaceFirst在我的jdk版本上使用StringBuffer创建一个新字符串。因此,假设正则表达式和替换字符串的长度很小,由于字符串复制,它是O(n),n是初始的长度:

public String replaceFirst(String replacement) {
    if (replacement == null)
        throw new NullPointerException("replacement");
    reset();
    if (!find())
        return text.toString();
    StringBuffer sb = new StringBuffer();
    appendReplacement(sb, replacement);
    appendTail(sb);
    return sb.toString();
}

注意:

  • 正则表达式匹配本身应该是O(1)。
  • 如果没有匹配,则replaceFirst转到return text.toString(),返回字符串本身:如果没有匹配,则为O(1)操作
  • startsWith + substring曾经是O(1)(当substring是字符串的视图时,即直到Java 7u5)但现在也是O(n)(从Java 7y6 substring通过复制底层char数组创建一个新字符串。

<强>更新

我已经快速测试了操作的性能,通过使用^锚点和没有锚点的正则表达式将空字符串和长字符串与正则表达式匹配来确认上述操作。结果(每次通话以纳秒为单位):

p1s1 (empty string, "^x")   47.561 nsec/op
p1s2 (long string, "^x")    50.753 nsec/op
p2s1 (empty string, "x")    47.526 nsec/op
p2s2 (long string, "x")    131.015 nsec/op

所以你可以看到"^x"正则表达式会在第一个字符处丢弃分析,因为时间(几乎)与空字符串或长字符串相同。

代码,使用jmh进行benhcmarking:

private String s1 = "";
private String s2 = "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa" +
                    "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa" +
                    "x";
private static final Pattern p1 = Pattern.compile("^x");
private static final Pattern p2 = Pattern.compile("x");

@GenerateMicroBenchmark
public boolean p1s1() { return p1.matcher(s1).find(); }

@GenerateMicroBenchmark
public boolean p1s2() { return p1.matcher(s2).find(); }

@GenerateMicroBenchmark
public boolean p2s1() { return p2.matcher(s1).find(); }

@GenerateMicroBenchmark
public boolean p2s2() { return p2.matcher(s2).find(); }