当前位置: 首页 > 面试题库 >

StringBuilder与.concat与“ +”运算符的相对性能在eclipse中是否与命令行不同?

艾仲渊
2023-03-14
问题内容

我正在阅读有关Java编译器如何在可能的情况下将与“ +”运算符连接的字符串编译为StringBuilder实例的方法,以及如何使简单的“
+”运算符更好地使用,因为它们可以编译为相同的代码。(除了在while循环中构建字符串时,在这种情况下,显然最好使用StringBuilder。)

我还读到,字符串上的.concat方法一直都是最糟糕的选择(以至于它被Findbugs变成了bug
!)。

因此,我决定自己测试一下,在eclipse中编写一个Java类进行测试。我的结果让我有些惊讶。我发现,如果我在Eclipse和命令行中使用并运行它们,则不同的方法相对较快或较慢。

首先,我的日食结果是:

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和我的命令行都引用了完全相同的一个。我知道的唯一区别是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()));
    }

}

问题答案:

在Oracle JDK 1.7(javac 1.7.0_17)上,仍然使用来实现“ +”运算符StringBuilder,如javap -c在类上运行以获取字节码所示(仅在此处显示循环):

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为a String

因此,问题就来了:为什么对于相同的代码,您的测试为什么会显示出如此不同的结果?或更完整地说, 这为什么不是有效的微基准 。以下是一些可能的原因:

  • 您正在计算挂钟时间。这意味着您实际上正在衡量运行测试时JVM所做的一切。其中包括垃圾回收(这很重要,因为您要创建大量垃圾)。您可以通过获取线程CPU时间来减轻这种情况。
  • 您无需验证HotSpot何时或是否在编译方法。这就是为什么您应该在进行任何微基准测试之前进行预热阶段的原因:基本上,先运行main()多次,然后再运行实际测试。


 类似资料:
  • 问题内容: 我正在阅读“ 更好,更快,更轻便的Java ”(作者Bruce Tate和Justin Gehtland),并且熟悉敏捷类型团队的可读性要求,例如Robert Martin在其干净的编码书中讨论的内容。在我现在所在的团队中,已明确告知我不要使用运算符,因为它会在运行时创建额外的(和不必要的)字符串对象。 但是,这篇写于‘04的文章讨论了对象分配如何与10条机器指令相关。(基本上免费)

  • 问题内容: 对于字符串连接,我们可以使用or或concat运算符。 我尝试了以下性能测试,发现更快,更有效地进行字符串连接。 字符串连接比较100,000次 : 结果 如果比运算符快,那么什么时候应该使用串联运算符? 问题答案: concat方法始终会生成一个带有串联结果的新String。 plus运算符由StringBuilder创建来支持,它会附加所需的所有String值,并进一步对toStr

  • 问题内容: 假设字符串a和b: 在幕后,他们是同一回事吗? 这里将concat反编译为参考。我也希望能够反编译该+运算符,以查看其作用。 问题答案: 不,不是。 首先,语义上略有不同。如果a是null,则抛出一个,但a+=b将把原来的值a就好像它是null。此外,该方法仅接受值,而+操作员会将参数无提示地转换为String(使用对象的方法)。因此,该concat()方法在接受方面更加严格。 要深入

  • 本文向大家介绍eclipse 中的javac命令与java命令,包括了eclipse 中的javac命令与java命令的使用技巧和注意事项,需要的朋友参考一下 eclipse中的javac命令与java命令 一、eclipse的javac命令:当eclipse对.java(源文件)文件进行保存操作时(快捷键ctrl+s),会执行javac命令。见上图,Default output folder(默

  • 问题内容: 在实践中我可以在多大程度上依赖对象及其唯一性?例如: 是否意味着反之亦然?那相反呢? 保存某个地方以后使用(例如,放入某个注册表而不是对象本身)有多安全? (作为针对Python规范的建议规范编写:是具有相同id()相同对象的对象,是is运算符,未绑定方法对象) 问题答案: 根据文档,只能保证an是唯一的 在特定对象的生命周期内,以及 在特定的解释器实例中 因此, 比较s是不安全的,除