我们必须一直为日志输出构建字符串等等。在JDK版本中,我们已经学习了什么时候使用StringBuffer(许多追加,线程安全)和StringBuilder(许多追加,非线程安全)。
使用String.format()有什么建议?它是有效的,还是我们被迫坚持在性能很重要的一行程序中使用连接?
例如,丑陋的老式风格,
String s = "What do you get if you multiply " + varSix + " by " + varNine + "?";
vs.整洁的新样式(字符串。格式,可能更慢),
String s = String.format("What do you get if you multiply %d by %d?", varSix, varNine);
注意:我的特定用例是代码中的数百个“一行”日志字符串。它们不涉及循环,所以StringBuilder太重量级了。我对String.format()特别感兴趣。
我使用了hhafez的代码并添加了一个内存测试:
private static void test() {
Runtime runtime = Runtime.getRuntime();
long memory;
...
memory = runtime.freeMemory();
// for loop code
memory = memory-runtime.freeMemory();
我为每一种方法分别运行这个程序,'+'操作符,String。format和StringBuilder(调用toString()),因此所使用的内存不会受到其他方法的影响。
我添加了更多的连接,使字符串为“Blah”+ I +“Blah”+ I +“Blah”+ I +“Blah”。
结果如下(平均每次5次):
Approach |
Time(ms) |
Memory allocated (long) |
+ operator |
747 |
320,504 |
String.format |
16484 |
373,312 |
StringBuilder |
769 |
57,344 |
我们可以看到String +和StringBuilder在时间上实际上是相同的,但是StringBuilder在内存使用上要高效得多。
当我们在足够短的时间间隔内有许多日志调用(或任何其他涉及字符串的语句)时,这是非常重要的,因此垃圾收集器将无法清理+操作符导致的许多字符串实例。
顺便说一句,在构造消息之前,不要忘记检查日志级别。
结论:
我将继续使用StringBuilder。
我有的是时间,有的是生活。
在您的示例中,性能可能没有太大不同,但还有其他问题需要考虑:即内存碎片。连接操作也在创建一个新字符串,即使它是临时的(GC需要时间,而且工作量更大)。String.format()可读性更强,涉及的碎片更少。
此外,如果你经常使用特定的格式,不要忘记你可以直接使用Formatter()类(所有String.format()所做的是实例化一个使用Formatter实例)。
此外,还应该注意其他一些事情:小心使用substring()。例如:
String getSmallString() {
String largeString = // load from file; say 2M in size
return largeString.substring(100, 300);
}
这个大字符串仍然在内存中,因为这就是Java子字符串的工作方式。一个更好的版本是:
return new String(largeString.substring(100, 300));
or
return String.format("%s", largeString.substring(100, 300));
如果你同时做其他事情,第二种形式可能更有用。
Java的字符串。格式如下:
它解析格式字符串,分解成一个格式块列表
它迭代格式块,呈现为一个StringBuilder,这基本上是一个数组,根据需要调整自己的大小,通过复制到一个新的数组。这是必要的,因为我们还不知道分配最终字符串的大小
StringBuilder.toString()将他的内部缓冲区复制到一个新的String中
如果这个数据的最终目的地是一个流(例如,渲染一个网页或写入一个文件),你可以将格式块直接组装到你的流中:
new PrintStream(outputStream, autoFlush, encoding).format("hello {0}", "world");
我推测优化器将优化掉格式字符串处理。如果是这样,就剩下了与手动展开String相同的平摊性能。格式转换为StringBuilder。
To expand/correct on the first answer above, it's not translation that String.format would help with, actually.
What String.format will help with is when you're printing a date/time (or a numeric format, etc), where there are localization(l10n) differences (ie, some countries will print 04Feb2009 and others will print Feb042009).
With translation, you're just talking about moving any externalizable strings (like error messages and what-not) into a property bundle so that you can use the right bundle for the right language, using ResourceBundle and MessageFormat.
Looking at all the above, I'd say that performance-wise, String.format vs. plain concatenation comes down to what you prefer. If you prefer looking at calls to .format over concatenation, then by all means, go with that.
After all, code is read a lot more than it's written.