Java性能String.indexOf(char)vs String.indexOf(单个字符串)

时间:2015-11-11 08:34:44

标签: java string performance

我认为String.indexOf(char)比...更快一点 String.indexOf(String)使用单个字符&单个字符串(例如,' x'&" x")

为了确保我的猜测,我编写了简单的测试代码,如下所示。

public static void main(String[] args) {
    IndexOfTest test = new IndexOfTest(Integer.parseInt(args[0]));

    test.run();
}

public IndexOfTest(int loop) {
    this.loop = loop;
}

public void run() {
    long start, end;
    start = System.currentTimeMillis();
    for(int i = 0 ; i < loop ; i++) {
        alphabet.indexOf("x");
    }
    end = System.currentTimeMillis();
    System.out.println("indexOf(String) : " + (end - start) + "ms");

    start = System.currentTimeMillis();
    for(int i = 0 ; i < loop ; i++) {
        alphabet.indexOf('x');
    }
    end = System.currentTimeMillis();
    System.out.println("indexOf(char) : " + (end - start) + "ms");

}

alphabet是String变量,其中包含&#34; abcd ... xyzABCD ... XYZ&#34;。

从这段代码中,我得到了这样的结果表......

loop     10^3  10^4  10^5  10^6  10^7

String      1     7     8     9     9

char        1     2     5    10    64

String.indexOf(String)看起来像收敛到9ms,但是String.indexOf(char)呈指数级增长。

我很困惑。在这种情况下是否有使用String的优化? 或者我如何弄清楚这个结果?

更新

我用以下两种基准方法运行jmh。每个方法都调用一个indexOf方法。

@State(Scope.Thread)
public class MyBenchmark {
    private String alphabet = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ";

    @Benchmark
    public void indexOfString() {
        alphabet.indexOf("x");
    }

    @Benchmark
    public void indexOfChar() {
    alphabet.indexOf('x');
    }
}

结果:

Benchmark                   Mode  Cnt           Score        Error  Units
MyBenchmark.indexOfChar    thrpt   30   142106399.525 ±  51360.808  ops/s
MyBenchmark.indexOfString  thrpt   30  2178872840.575 ± 864573.421  ops/s

此结果还显示indexOf(String)更快..

我认为是时候考虑隐藏优化了

有什么想法吗?

1 个答案:

答案 0 :(得分:9)

您的JMH测试不正确,因为您没有使用结果,因此JIT编译器可以(或可以不)删除indexOf调用。在你的情况下,似乎JIT编译器确定indexOf(String)没有副作用并且完全删除了这个调用,但对indexOf(char)没有做同样的事情。总是消耗结果(最简单的方法是从基准测试返回)。这是我的版本:

import java.util.*;
import java.util.concurrent.TimeUnit;

import org.openjdk.jmh.annotations.*;

@State(Scope.Benchmark)
@BenchmarkMode(Mode.AverageTime)
@OutputTimeUnit(TimeUnit.NANOSECONDS)
@Warmup(iterations = 5, time = 500, timeUnit = TimeUnit.MILLISECONDS)
@Measurement(iterations = 10, time = 500, timeUnit = TimeUnit.MILLISECONDS)
@Fork(3)
public class IndexOfTest { 
    private String str;
    private char c;
    private String s;

    @Setup
    public void setup() {
        str = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz";
        c = 'z';
        s = "z";
    }

    @Benchmark
    public int indexOfChar() {
        return str.indexOf('z');
    }

    @Benchmark
    public int indexOfString() {
        return str.indexOf("z");
    }

    @Benchmark
    public int indexOfCharIndirect() {
        return str.indexOf(c);
    }

    @Benchmark
    public int indexOfStringIndirect() {
        return str.indexOf(s);
    }
}

我测试了相同的东西,但添加了两个间接测试:当从字段加载搜索char或String时,因此在JIT编译期间其确切值是未知的。结果如下(Intel x64):

# JMH 1.11.2 (released 27 days ago)
# VM version: JDK 1.8.0_45, VM 25.45-b02
Benchmark                          Mode  Cnt   Score   Error  Units
IndexOfTest.indexOfChar            avgt   30  25,364 ± 0,424  ns/op
IndexOfTest.indexOfCharIndirect    avgt   30  25,287 ± 0,210  ns/op
IndexOfTest.indexOfString          avgt   30  24,370 ± 0,100  ns/op
IndexOfTest.indexOfStringIndirect  avgt   30  27,198 ± 0,048  ns/op

如您所见,无论是直接访问还是间接访问,indexOfChar都以相同的方式执行。 indexOfString对于直接访问来说稍微快一些,但对于间接访问来说稍慢一些。那是因为indexOf(String)是JVM内在的:它的Java代码实际上被JIT编译器替换为高效的内联实现。对于在JIT编译时已知的常量字符串,可以生成更高效的代码。

一般来说,至少对于这样的短字符串没有太大的区别。因此,您可以使用这些方法中的任何一种进行单符号匹配。