我也读过这个字符串的.concat方法是最糟糕的choice all the time(这么多,所以它被修改为一个错误Findbugs!).
所以我决定自己在Eclipse中写一个java类.我的结果让我感到惊讶我发现,不同的方法比较快或慢,如果我遵守和运行它们在日食和命令行.
首先我的日食结果是:
the total millis to concatenate with + was: 12154 the total millis to concatenate with .concat was: 8840 the total millis to concatenate with StringBuilder was: 11350 the total millis to concatenate with StringBuilder with a specified size was: 5611
所以在eclipse的StringBuilder中,指定的大小是最快的,其次是.concat(奇怪的),那么StringBuilder和“”连接几乎是一样的.
然而,我在命令行上的结果是:
the total millis to concatenate with + was: 4139 the total millis to concatenate with .concat was: 8590 the total millis to concatenate with StringBuilder was: 10888 the total millis to concatenate with StringBuilder with a specified size was: 6033
所以当我从commnad行编译并运行时,“”运算符显然是最快的,其次是String builder的大小,然后是concat,最后是正常的StringBuilder!
这对我来说没有意义.显然所有的stackoverflow答案我读到说操作符编译成正常的旧StringBuilder实例必须是过时的.
有人知道这里真的发生了什么吗?
我正在使用jdk1.7.0_07,只要我能告诉eclipse和我的命令行都引用了完全相同的一个.我所知道的唯一的区别就是使用“javaw”,但是从我所看到的,这不应该有所作为.
这是我的测试类,如果你想验证我没有做错什么,但我很确定它是坚实的.
public class Test { static final int LOOPS = 100000000; static final String FIRST_STRING = "This is such"; static final String SECOND_STRING = " an awesomely cool "; static final String THIRD_STRING = "to write string."; /** * @param args */ public static void main(String[] args) { Test.plusOperator(); Test.dotConcat(); Test.stringBuilder(); Test.stringBuilderSizeSpecified(); } public static void plusOperator() { String localOne = FIRST_STRING; String localTwo = SECOND_STRING; String localThree = THIRD_STRING; Calendar startTime = Calendar.getInstance(); for (int x = 0; x < LOOPS; x++) { String toPrint = localOne + localTwo + localThree; } Calendar endTime = Calendar.getInstance(); System.out.println("the total millis to concatenate with + was: " + (endTime.getTimeInMillis() - startTime.getTimeInMillis())); } public static void stringBuilder() { String localOne = FIRST_STRING; String localTwo = SECOND_STRING; String localThree = THIRD_STRING; Calendar startTime = Calendar.getInstance(); for (int x = 0; x < LOOPS; x++) { StringBuilder toBuild = new StringBuilder() .append(localOne) .append(localTwo) .append(localThree); } Calendar endTime = Calendar.getInstance(); System.out.println("the total millis to concatenate with StringBuilder was: " + (endTime.getTimeInMillis() - startTime.getTimeInMillis())); } public static void stringBuilderSizeSpecified() { String localOne = FIRST_STRING; String localTwo = SECOND_STRING; String localThree = THIRD_STRING; Calendar startTime = Calendar.getInstance(); for (int x = 0; x < LOOPS; x++) { StringBuilder toBuild = new StringBuilder(50) .append(localOne) .append(localTwo) .append(localThree); } Calendar endTime = Calendar.getInstance(); System.out.println("the total millis to concatenate with StringBuilder with a specified size was: " + (endTime.getTimeInMillis() - startTime.getTimeInMillis())); } public static void dotConcat() { String localOne = FIRST_STRING; String localTwo = SECOND_STRING; String localThree = THIRD_STRING; Calendar startTime = Calendar.getInstance(); for (int x = 0; x < LOOPS; x++) { String toPrint = localOne.concat(localTwo).concat(localThree); } Calendar endTime = Calendar.getInstance(); System.out.println("the total millis to concatenate with .concat was: " + (endTime.getTimeInMillis() - startTime.getTimeInMillis())); } }
解决方法
public static void plusOperator(); Code: 16: iload 4 18: ldc #10 // int 100000000 20: if_icmpge 53 23: new #11 // class java/lang/StringBuilder 26: dup 27: invokespecial #12 // Method java/lang/StringBuilder."<init>":()V 30: aload_0 31: invokevirtual #13 // Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder; 34: aload_1 35: invokevirtual #13 // Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder; 38: aload_2 39: invokevirtual #13 // Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder; 42: invokevirtual #14 // Method java/lang/StringBuilder.toString:()Ljava/lang/String; 45: astore 5 47: iinc 4,1 50: goto 16 public static void stringBuilder(); Code: 16: iload 4 18: ldc #10 // int 100000000 20: if_icmpge 50 23: new #11 // class java/lang/StringBuilder 26: dup 27: invokespecial #12 // Method java/lang/StringBuilder."<init>":()V 30: aload_0 31: invokevirtual #13 // Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder; 34: aload_1 35: invokevirtual #13 // Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder; 38: aload_2 39: invokevirtual #13 // Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder; 42: astore 5 44: iinc 4,1 47: goto 16
这两者之间的唯一区别是,带有“”的版本将StringBuilder转换为循环中的String.
所以问题成了:为什么你的测试显示相同代码的这样不同的结果.或者更完整的,为什么这不是一个有效的微基准.以下是一些可能的原因:
你正在计算挂钟时间.这意味着您实际上是在运行测试时测量JVM所做的一切.其中包括垃圾收集(这很重要,因为您正在创建大量垃圾).您可以通过获取线程cpu时间来缓解此问题.>您不会验证HotSpot是在何时或正在编译方法.这就是为什么你应该在任何微基准测试之前做一个预热阶段:基本上,在运行你的实际测试之前多次运行main().