哪种toString()方法在性能方面可以使用?

6

我正在进行一个性能增强项目。我有一个疑问,在我们进行过程时,我们倾向于跟踪使用的DTO和实体的当前状态。因此,为此我们已经在所有POJO中包含了toString()方法。我现在以三种不同的方式实现了toString(),它们如下:

public String toString() {
    return "POJO :" + this.class.getName() + " RollNo :" + this.rollNo + " Name :" + this.name;
}

public String toString() {
    StringBuffer buff = new StringBuffer("POJO :").append(this.class.getName()).append(" RollNo :").append(this.rollNo).append(" Name :").append(this.name);
    return buff.toString();
}

public String toString() {
        StringBuilder builder = new StringBuilder("POJO :").append(this.class.getName()).append(" RollNo :").append(this.rollNo).append(" Name :").append(this.name);
        return builder .toString();
    }

请问有人可以帮我找出哪一个最好,应该用于提高性能。


1
我相信你可以在2分钟内编写一个基准测试。为什么要猜测,当你可以进行测量呢? - Stephen
6
基准测试能够展示结果,但理论上了解这些信息也是很有好处的。 - Bozho
可能是Java中toString()方法中的StringBuilder vs String concatenation的重复问题。 - polygenelubricants
我正在进行一个性能增强项目 - 使用分析器,确定哪个代码部分值得关注和优化。 - polygenelubricants
4个回答

13
在这种情况下,带有+的方法是可以的。它更易读,并且与StringBuilder/StringBuffer版本相比,性能也同样好,因为它不会在循环内发生。
如果你正在循环中构建一个String,那么很可能应该使用StringBuilder。只有在需要synchronized特性时才使用StringBuffer,这种情况并不经常发生。
简单地说(并非总是如此,但是是一个好的经验法则),除非你正在使用+=String,否则你真的不需要StringBuilder/StringBuffer

相关问题


String.format选项

一个经常被忽视的选项是使用String.format。它看起来像这样:

return String.format("POJO : %s RollNo %s : Name : %s",
   this.getClass().getName(),
   this.rollNo,
   this.name
);

我认为这是最易读和易于维护的版本。

它是否更快?也许是,也许不是。在常见用例场景中,对于像 toString() 这样的东西通常并不重要。追求易读性,只有在分析表明必要时才进行优化。

API 链接


关于类字面量

我已经纠正了原始代码中的语法错误,将 this.class(不能编译)更正为 this.getClass()

另请参阅

相关问题


4
“这个更快吗?” - 几乎肯定不会更快,因为format需要解析格式字符串,而调用者需要分配并填充一个varargs数组。但正如你所说,这可能并不重要。 - Stephen C
@Stephen:是的,如果使用 format 更快,我会感到非常惊讶,但我尽量不去培养这种本能,而是相信性能分析工具。 - polygenelubricants
1
此外,我认为您可以将类名缓存到static final String中,但我怀疑这不会有太大的差异。 OP应该使用分析器并找到一个真正值得优化的代码片段。 - polygenelubricants

9

使用第一个,因为它更易读。

但除此之外,没有什么影响。

  • 在这种情况下,使用 StringBuilder+ 是等效的,因为编译器将重载的 + 运算符转换为 StringBuilder

  • StringBuffer 会更慢,因为它的方法是 synchronized 的,但由于编译器可能使用 逃逸分析(更具体地说,是同步消除)(在较新版本中),它会自动删除 synchronized 关键字。(请参阅 JDK 6u14 发行说明 了解逃逸分析)


哇!哪个JVM运行时支持同步省略?这是我第一次听说这样一个(酷!)功能! - polygenelubricants
2
@polygenelubricants 自JDK 6u14以来,它已经可用,但我认为默认情况下未开启。请参见http://java.sun.com/javase/6/webnotes/6u14.html了解如何打开它。 - Bozho
3
不要这么快。不幸的是,基于逃逸分析的优化在更新14中启用了,但在更新18中又被禁用了,将在未来的某个时间重新安装。 - Stephen C
1
逃逸分析在6u20中存在,但我不知道它对这个分析做了什么,因为它似乎并没有使其更快。 ;) - Peter Lawrey

3

使用第一个。我会解释为什么。

天真的看法是使用最后一个。没有理由使用第二个。一个 StringBuffer 和一个 StringBuilder 是一样的,除了它有一个来自 synchronized 锁的性能损失。但不要将其放在一行上。这更易读:

public String toString() {
  StringBuilder out = new StringBuilder(("POJO :");
  out.append(this.getClass().getName());
  out.append(" RollNo :");
  out.append(this.rollNo);
  out.append(" Name :");
  out.append(this.name);
  return out.toString();
}

话虽如此,你必须小心这种微优化。为什么?因为编译器通常会自动完成这个过程。所以写出最易读的代码,让编译器进行优化。

所以主要的教训是:不要进行微观优化

尽管如此,使用1或3可能并不重要。在应用程序中,toString()方法很少被大量使用。更常见的情况是在错误消息中使用,希望这种情况很少发生。


6
为什么要显式地使用 StringBuilder?第一种方式会隐式地使用它,而且没有多余的代码。 - Jon Skeet
我知道编译器会将第一版本转换为第三版本,但是调试生成的版本非常困难,因为发生的事情与源代码中所看到的不同,所以我倾向于使用第三个版本。 - Sean Patrick Floyd
@Seanizer 你经常需要调试你的toString()方法吗?通常我会在我的toString中的一行上设置断点,然后查看调试器中的变量值,它会告诉我可能会导致异常的原因。 - Peter Lawrey
1
@seanizer:你可以进一步运用这种逻辑,认为编写代码的唯一正确方式是使用汇编语言,因为那才是“真正发生”的事情,对吧?不,我不同意。抽象是好的。无视抽象是不好的,但抽象本身是好的。越多越好。 - polygenelubricants
@Peter,@Poly,我不经常调试toString方法,但我经常调试第三方库的方法,在这些方法调用中发生字符串连接,我必须在eclipse中进行大量的F5 / F7操作,以便不错过我感兴趣的实际语句。是的,抽象是好的,但在这种情况下,抽象有一个常常困扰我的代价。汇编点是无关紧要的,因为我的调试器幸运地没有通过汇编调用。但“发生了什么”是一个糟糕的措辞选择,我同意。 - Sean Patrick Floyd

2

在我看来,这更易读

public String toString() { 
    return String.Format("POJO : {0} RollNo : {1} Name : {2}",
                          this.getClass().getName(),
                          this.rollNo,
                          this.name);
} 

可能在C#中是这样的,但在Java中不是。此外,this.class无法编译。 - polygenelubricants
在Java中,使用MessageFormat.format(...)具有相同的参数语法(实际上我会使用this.getClass()而不是getClass().getName(),因为在需要之前没有必要评估toString())。顺便说一句,this.class不会编译,正如许多人已经提到的那样。 - Sean Patrick Floyd

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接