为什么在 StringBuilder 构造函数中使用连接比调用 append() 快 100 倍?

Why is this usage of concatentation in StringBuilder's constructor 100X faster than calling append()?

我偶然发现了一个与 StringBuilderappend 方法相关的奇怪性能问题。我注意到似乎是一个愚蠢的错误 - 使用 StringBuilder 构造函数期间的字符串连接(这甚至在 NetBeans IDE 中显示为警告)。

版本 1

int hash = -1;     //lazily computed, also present in Version 2
int blockID = ...  //0 to 1000, also present in Version 2
int threadID = ... //0 to 1000, also present in Version 2
boolean hashed = false;       //also present in Version 2

@Override
public int hashCode(){
    if(!hashed){
        StringBuilder s = new StringBuilder(blockID+":"+threadID);
        hash = s.toString().hashCode();
        hashed= true;
    }

    return hash;
}

数百万个此类对象是在运行时创建的,所以我认为通过进行以下更改,它会提供一些加速:

版本 2

@Override
public int hashCode(){
    if(!hashed){
        StringBuilder s = new StringBuilder(blockID);
        s.append(":");
        s.append(threadID);
        hash = s.toString().hashCode();
        hashed = true;
    }

    return hash;
}

错了!事实证明,版本 2 确实比版本 1 慢 100 倍。为什么???

附加信息

我正在根据 Java 6(客户要求)进行编译,并且我正在使用 Oracle 的 JVM。

我的性能测试涉及创建一百万个这样的对象并将它们放入 HashMap 中。使用版本 1 需要半秒,但使用版本 2 将近 50 秒。

因为您无意中设置了 StringBuilder 的初始容量,而不是向其附加 blockID。参见 constructor documentation here

public StringBuilder(int capacity)

Constructs a string builder with no characters in it and an initial capacity specified by the capacity argument.

试试这个:

StringBuilder s = new StringBuilder(9);
s.append(blockID).append(':').append(threadID);

您需要检查您的测试,因为您的第一个案例实际上正在做。

public int hashCode(){
    if(!hashed){
        StringBuilder s = new StringBuilder(
                  new StringBuilder(blockID).append(":").append(threadID).toString());
        hash = s.toString().hashCode();
        hashed= true;
    }

    return hash;
}

换句话说,它在第二种情况下做所有事情,而且会做更多,所以它会更慢。

简而言之,我怀疑你的测试是错误的,并不是你的表现越来越好。