6

每次我必须将 a 转换int为 a时String,我都会选择""+aInteger.toString(a)。现在我想知道哪种方式更快,所以我编写了一个简单的基准测试,调用 function_1、function_2 和 function_3 10000000 次,并打印处理这些函数需要多长时间。以下是功能:

public static String i="";
public static String j="";
public static String k="";

public static void function_1()
{
    i=Integer.toString(getOne());
}

public static void function_2()
{
    j=""+1;
}

public static void function_3()
{
    j=""+getOne();
}

public static int getOne()
{
    return 1;
}

输出是:

Benchmarking starting...
Executing function_1 10000000 time(s)...
Done executing function_1 in 476 ms.
Executing function_2 10000000 time(s)...
Done executing function_2 in 8 ms.
Executing function_3 10000000 time(s)...
Done executing function_3 in 634 ms.
Benchmarking complete!

我认为function_2是如此之快,因为它被编译为

public static void function_2()
{
    j="1";
}

所以为了避免这种情况,我使用了这个函数getOne()。但这里是有趣的部分(对我来说):function_3必须在不使用原始toString方法的情况下编译Object(在这种情况下Integer.toString(1)因为int是原始的)。我的问题是:编译器实际上是如何威胁的""+1,所以它比调用慢Integer.toString(1)

4

3 回答 3

5

""并且1在编译时已知。这就是为什么 infunction_2 "" + 1真正被"1"while 转换为字节码所取代的原因。

getOne()结果在编译时是未知的,因此连接将在运行时完成。但是因为连接(+)效率不高,编译器很可能会将其更改为StringBuilder.append()基于实现。

不相信我?试试:javap -c ClassName.class你会看到这样的:

public static void function_2();
Code:
   0: ldc           #39                 // String 1
   2: putstatic     #16                 // Field j:Ljava/lang/String;
   5: return        


public static void function_3();
Code:
   0: new           #42                 // class java/lang/StringBuilder
   3: dup           
   4: invokespecial #44                 // Method java/lang/StringBuilder."<init>":()V
   7: invokestatic  #28                 // Method getOne:()I
  10: invokevirtual #45                 // Method java/lang/StringBuilder.append:(I)Ljava/lang/StringBuilder;
  13: invokevirtual #49                 // Method java/lang/StringBuilder.toString:()Ljava/lang/String;
  16: putstatic     #16                 // Field j:Ljava/lang/String;
  19: return 

function_2()只有一个字符串“1”,而 function_3 有所有这些方法调用,里面有额外的 StringBuilder :)

请记住,运行时可能会发生一些优化,但这种行为是 JVM 并且它依赖于配置。

于 2013-03-27T21:05:31.213 回答
1

我在 10,000,000 次迭代中测试了以下函数:

public static void no_func_maybe_constant()
{
    j= "" + 1;
}

public static void no_func_no_constant()
{
    j = "";
    j = j + 1;
}

public static void yes_func_maybe_constant()
{
    j = "" + getOne();
}

public static void yes_func_no_constant()
{
    j = "";
    j = j + getOne();
}

我的结果:

no_func_maybe_constant Took 0.028058674s
no_func_no_constant Took 1.449465242s
yes_func_maybe_constant Took 1.275561897s
yes_func_no_constant Took 1.263362257s

不调用函数和调用函数之间的区别确实可以忽略不计,所以在它的情况下似乎"" + 1确实在进行一些编译时常量计算。有趣的是,如果没有功能,有时会花费更少的时间......

于 2013-03-27T20:47:58.697 回答
0

2 和 3 之间的差异可能是由于必须调用方法来获取整数。当您调用一个方法时,它会创建一个新的激活记录,这会使调用堆栈变得复杂,因此除非 JVM 的 JIT 能够将静态函数调用内联到单个返回值(几乎可以肯定这里不会发生),否则这里会发生更多事情。

于 2013-03-27T20:50:37.440 回答