我们必须一直为日志输出构建字符串等等。在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()特别感兴趣。
仅从日志的角度看另一个角度。
我在这个帖子上看到了很多关于登录的讨论,所以我想在回答中加入我的经验。也许有人会觉得有用。
我猜使用格式化程序进行日志记录的动机来自于避免字符串连接。基本上,如果你不打算记录它,你不希望有字符串连接的开销。
实际上不需要concat/format,除非您想记录日志。假设我这样定义一个方法
public void logDebug(String... args, Throwable t) {
if(debugOn) {
// call concat methods for all args
//log the final debug message
}
}
在这种方法中,如果它是一个调试消息并且debugOn = false,则根本不会真正调用canat /formatter
尽管在这里使用StringBuilder而不是formatter会更好。我们的主要动机就是避免这些。
同时,我不喜欢为每个日志语句添加“if”块
它会影响可读性
减少单元测试的覆盖率——当你想要确保每一行都经过测试时,这会让人感到困惑。
因此,我更喜欢用上面的方法创建一个日志实用程序类,并在任何地方使用它,而不用担心性能损失和其他与之相关的问题。
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.
我使用了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。
我有的是时间,有的是生活。
下面是修改后的hhafez入口。它包括一个字符串构建器选项。
public class BLA
{
public static final String BLAH = "Blah ";
public static final String BLAH2 = " Blah";
public static final String BLAH3 = "Blah %d Blah";
public static void main(String[] args) {
int i = 0;
long prev_time = System.currentTimeMillis();
long time;
int numLoops = 1000000;
for( i = 0; i< numLoops; i++){
String s = BLAH + i + BLAH2;
}
time = System.currentTimeMillis() - prev_time;
System.out.println("Time after for loop " + time);
prev_time = System.currentTimeMillis();
for( i = 0; i<numLoops; i++){
String s = String.format(BLAH3, i);
}
time = System.currentTimeMillis() - prev_time;
System.out.println("Time after for loop " + time);
prev_time = System.currentTimeMillis();
for( i = 0; i<numLoops; i++){
StringBuilder sb = new StringBuilder();
sb.append(BLAH);
sb.append(i);
sb.append(BLAH2);
String s = sb.toString();
}
time = System.currentTimeMillis() - prev_time;
System.out.println("Time after for loop " + time);
}
}
循环391之后的时间
循环4163之后的时间
循环227之后的时间