给定下面的2个toString()实现,哪个是首选的:
public String toString(){
return "{a:"+ a + ", b:" + b + ", c: " + c +"}";
}
or
public String toString(){
StringBuilder sb = new StringBuilder(100);
return sb.append("{a:").append(a)
.append(", b:").append(b)
.append(", c:").append(c)
.append("}")
.toString();
}
?
更重要的是,鉴于我们只有3个属性,它可能不会有什么不同,但在什么时候你会从+ concat切换到StringBuilder?
这取决于字符串的大小。
请看下面的例子:
static final int MAX_ITERATIONS = 50000;
static final int CALC_AVG_EVERY = 10000;
public static void main(String[] args) {
printBytecodeVersion();
printJavaVersion();
case1();//str.concat
case2();//+=
case3();//StringBuilder
}
static void case1() {
System.out.println("[str1.concat(str2)]");
List<Long> savedTimes = new ArrayList();
long startTimeAll = System.currentTimeMillis();
String str = "";
for (int i = 0; i < MAX_ITERATIONS; i++) {
long startTime = System.currentTimeMillis();
str = str.concat(UUID.randomUUID() + "---");
saveTime(savedTimes, startTime);
}
System.out.println("Created string of length:" + str.length() + " in " + (System.currentTimeMillis() - startTimeAll) + " ms");
}
static void case2() {
System.out.println("[str1+=str2]");
List<Long> savedTimes = new ArrayList();
long startTimeAll = System.currentTimeMillis();
String str = "";
for (int i = 0; i < MAX_ITERATIONS; i++) {
long startTime = System.currentTimeMillis();
str += UUID.randomUUID() + "---";
saveTime(savedTimes, startTime);
}
System.out.println("Created string of length:" + str.length() + " in " + (System.currentTimeMillis() - startTimeAll) + " ms");
}
static void case3() {
System.out.println("[str1.append(str2)]");
List<Long> savedTimes = new ArrayList();
long startTimeAll = System.currentTimeMillis();
StringBuilder str = new StringBuilder("");
for (int i = 0; i < MAX_ITERATIONS; i++) {
long startTime = System.currentTimeMillis();
str.append(UUID.randomUUID() + "---");
saveTime(savedTimes, startTime);
}
System.out.println("Created string of length:" + str.length() + " in " + (System.currentTimeMillis() - startTimeAll) + " ms");
}
static void saveTime(List<Long> executionTimes, long startTime) {
executionTimes.add(System.currentTimeMillis() - startTime);
if (executionTimes.size() % CALC_AVG_EVERY == 0) {
out.println("average time for " + executionTimes.size() + " concatenations: "
+ NumberFormat.getInstance().format(executionTimes.stream().mapToLong(Long::longValue).average().orElseGet(() -> 0))
+ " ms avg");
executionTimes.clear();
}
}
输出:
java bytecode version:8
java.version: 1.8.0_144
[str1.concat(str2)]
average time for 10000 concatenations: 0.096 ms avg
average time for 10000 concatenations: 0.185 ms avg
average time for 10000 concatenations: 0.327 ms avg
average time for 10000 concatenations: 0.501 ms avg
average time for 10000 concatenations: 0.656 ms avg
Created string of length:1950000 in 17745 ms
[str1+=str2]
average time for 10000 concatenations: 0.21 ms avg
average time for 10000 concatenations: 0.652 ms avg
average time for 10000 concatenations: 1.129 ms avg
average time for 10000 concatenations: 1.727 ms avg
average time for 10000 concatenations: 2.302 ms avg
Created string of length:1950000 in 60279 ms
[str1.append(str2)]
average time for 10000 concatenations: 0.002 ms avg
average time for 10000 concatenations: 0.002 ms avg
average time for 10000 concatenations: 0.002 ms avg
average time for 10000 concatenations: 0.002 ms avg
average time for 10000 concatenations: 0.002 ms avg
Created string of length:1950000 in 100 ms
随着字符串长度的增加,+=和.concat的连接时间也会增加,后者效率更高,但仍然是非常量
这就是绝对需要StringBuilder的地方。
附注:我不认为什么时候在Java中使用StringBuilder是一个真正的复制。
这个问题讨论的是toString(),它在大多数情况下不会执行大字符串的连接。
2019年更新
自java8时代以来,情况发生了一些变化。现在看来(java13), +=的连接时间实际上与str.concat()相同。但是StringBuilder的连接时间仍然是固定的。(上面的原始帖子略有编辑,添加了更多详细的输出)
java bytecode version:13
java.version: 13.0.1
[str1.concat(str2)]
average time for 10000 concatenations: 0.047 ms avg
average time for 10000 concatenations: 0.1 ms avg
average time for 10000 concatenations: 0.17 ms avg
average time for 10000 concatenations: 0.255 ms avg
average time for 10000 concatenations: 0.336 ms avg
Created string of length:1950000 in 9147 ms
[str1+=str2]
average time for 10000 concatenations: 0.037 ms avg
average time for 10000 concatenations: 0.097 ms avg
average time for 10000 concatenations: 0.249 ms avg
average time for 10000 concatenations: 0.298 ms avg
average time for 10000 concatenations: 0.326 ms avg
Created string of length:1950000 in 10191 ms
[str1.append(str2)]
average time for 10000 concatenations: 0.001 ms avg
average time for 10000 concatenations: 0.001 ms avg
average time for 10000 concatenations: 0.001 ms avg
average time for 10000 concatenations: 0.001 ms avg
average time for 10000 concatenations: 0.001 ms avg
Created string of length:1950000 in 43 ms
值得注意的还有bytecode:8/java。与bytecode:8/java.version:8相比,Version:13组合具有良好的性能优势
Apache Commons-Lang有一个超级容易使用的ToStringBuilder类。它在处理附加逻辑以及格式化你想要的toString外观方面做得很好。
public void toString() {
ToStringBuilder tsb = new ToStringBuilder(this);
tsb.append("a", a);
tsb.append("b", b)
return tsb.toString();
}
将返回类似com.blah的输出。YourClass@abc1321f (a =, = foo)。
或者使用链接的更浓缩的形式:
public void toString() {
return new ToStringBuilder(this).append("a", a).append("b", b").toString();
}
或者如果你想使用反射来包含类的每个字段:
public String toString() {
return ToStringBuilder.reflectionToString(this);
}
如果需要,还可以自定义ToString的样式。