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

我认为当使用单个字符和单个字符串(例如,’x’和“x”)时, String.indexOf(char)String.indexOf(String)快一点

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

 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是具有“abcd … xyzABCD … XYZ”的字符串变量。

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

 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)更快..

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

任何想法?

您的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编译时已知的常量字符串,可以生成更高效的代码。

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