12.6 字符串的性能

字符串对 Java 非常重要,其性能在其他章节也已经讨论过,这里再强调几点。

字符串保留

  创建多个包含相同字符序列的字符串对象,这种情况很常见。没有必要在堆中为所有这些对象都分配空间;因为字符串是不可变的,所以重用现有的字符串往往更好。更多细节可参见第 7 章。

字符串编码

  Java 的字符串采用的是 UTF-16 编码,而其他地方多是使用其他编码,所以将字符串编码到不同的字符集的操作很常见。对于 Charset 类的 encode()decode() 方法而言,如果一次只处理一个或几个字符,它们会非常慢;务必完整缓存一些数据,再进行处理,本章前面也讨论过。

网络编码

  在编码静态字符串(来自 JSP 文件等地方)时,Java EE 应用服务器往往会特殊处理;更多细节可参见第 10 章。

字符串连接是另一个可能会出现性能问题的地方。考虑这样一个简单的字符串连接操作:

  1. String answer = integerPart + "." + mantissa;

这行代码实际上非常高效;javac 编译器的语法糖会将其转换为如下代码:

  1. String answer = new StringBuilder(integerPart).append(".").
  2. append(mantissa).toString();

不过问题来了,如果这个字符串是逐步构造起来的:

  1. String answer = integerPart;
  2. answer +=".";
  3. answer += mantissa;

那么这段代码就会被翻译为:

  1. String answer = new StringBuilder(integerPart).toString();
  2. answer = new StringBuilder(answer).append(".").toString();
  3. answer = new StringBuilder(answer).append(mantissa).toString();

所有那些临时的 StringBuilder 对象和中间的 String 对象都很低效。永远不要使用连接来构造字符串,除非能在逻辑意义上的一行代码内完成;也不要在循环内使用字符串连接,除非连接后的字符串不会用于下一次循环迭代。对于其他情况,应该总是使用 StringBuilder,以获得更好的性能。在第 1 章中我曾经说过,某种情况下“过早的优化”只是表示要“编写良好的代码”。这就是最好的例子。

12.6 字符串的性能 - 图1 快速小结

1. 一行的字符串连接代码性能很不错。

2. 对于多行的连接操作,一定要确保使用 StringBuilder