鉴于使用Android开发时,重用对象比创建新对象要好,删除
StringBuilder
的内容并重用它值得吗? StringBuilder b = new StringBuilder();
//build up a string
b.delete(0, b.length());
//reuse it
与以下内容相比:
StringBuilder b1 = new StringBuilder();
//build up a string
StringBuilder b2 = new StringBuilder();
//create a new one
#1 楼
重用不需要分配内存,因此可以像@Mike Nakis提到的那样更快。另一方面,它导致代码很丑陋:您将相同的变量用于两个(或多个)不同的目的,这可能会使读者感到困惑,使维护工作更加困难。除非您有充分的理由避免这样做。我的意思是,如果分析表明创建新的StringBuilder
实例是应用程序中的瓶颈,您就有充分的理由。如果您真的必须重用
StringBuilder
,请尝试使用帮助程序以不同的名称访问它们方法:public static StringBuilder reuseForBetterPerformance(final StringBuilder sb) {
sb.delete(0, sb.length());
return sb;
}
客户代码:
final StringBuilder b = new StringBuilder();
//build up a string
final StringBuilder c = reuseForBetterPerformance(b);
//reuse it
c.append(...)
它使代码更具可读性,因为方法名称说明了为什么要重用
StringBuilder
。评论
\ $ \ begingroup \ $
我不明白决赛的目的
\ $ \ endgroup \ $
– Xavier Combelle
2012年1月8日14:31
\ $ \ begingroup \ $
如果还没有定型,则可以创建一个新的返回它,但是我们想返回相同的对象但将其清空。
\ $ \ endgroup \ $
–luketorjussen
2012年1月8日在16:14
\ $ \ begingroup \ $
final帮助读者,因为他们知道引用始终指向同一实例,并且以后不会更改。虽然是programms.stackexchange.com/questions/115690/…,但是您可以在该主题中找到有关Programmers.SE的其他问题。
\ $ \ endgroup \ $
–palacsint
2012年1月8日17:25
\ $ \ begingroup \ $
辅助功能是一个很好的折衷方案。谢谢你的主意。
\ $ \ endgroup \ $
–克里斯托弗(Christopher)
2012年1月10日上午9:22
#2 楼
要添加一些硬数字,我编写了一个简单的测试应用程序:public static void main(String[] args) {
int iterations = 1000000;
int secondIterations = 25;
long start = System.currentTimeMillis();
for (int count = 0; count < iterations; count++) {
StringBuilder builder = new StringBuilder();
for (int secondCounter = 0; secondCounter < secondIterations; secondCounter++) {
builder.append("aaassssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssss");
}
}
System.out.println("Recreation took: " + Long.toString(System.currentTimeMillis() - start) + "ms");
start = System.currentTimeMillis();
StringBuilder builder = new StringBuilder();
for (int count = 0; count < iterations; count++) {
builder.delete(0, builder.length());
for (int secondCounter = 0; secondCounter < secondIterations; secondCounter++) {
builder.append("aaassssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssss");
}
}
System.out.println("Reuse took: " + Long.toString(System.currentTimeMillis() - start) + "ms");
}
这给出了以下输出:
Recreation took: 10594ms
Reuse took: 1937ms
所以,是的,因为重用确实确实更快,至少在以较小内存占用的紧密循环中进行测试的情况下。但是请记住,这些数字是使用一百万次迭代生成的。
请牢记两个规则:
首先要考虑可读性和可维护性。
第二...运行探查器。
在真正遇到性能问题之前,我在这里看到的这种差异完全不用担心。
评论
\ $ \ begingroup \ $
哇,快5倍。尽管我愿意打赌,这种差异的大部分是由于您的“娱乐”循环生成2.2 GB的垃圾,所以在“重用”循环产生大量垃圾的同时,请练习垃圾收集器(有损于此)。垃圾少了,几乎没有。
\ $ \ endgroup \ $
–迈克·纳基斯(Mike Nakis)
2015年9月23日在18:23
#3 楼
是的,即使在Android之外的广阔世界中,这当然也是值得的。根据有关
StringBuilder
的文档:每个字符串生成器都有一个容量。只要字符串生成器中包含的字符序列的长度不超过容量,就不必分配新的内部缓冲区。如果内部缓冲区溢出,则会自动变大。
这意味着删除
StringBuilder
的内容将不会对其char
的内部数组进行内存分配操作(它会将其长度设置为零并返回,并保持其最后的容量)。另外,您可以保存StringBuilder
对象本身。评论
\ $ \ begingroup \ $
感谢您的回复对我非常有帮助,因此在这里我有一个问题:1.字符串生成器的默认容量是多少。 2.因此,如果StringBuilder在该时间动态调整其缓冲区大小时会分配新的内存,请确认。
\ $ \ endgroup \ $
–Utsav
15年1月16日在6:45
\ $ \ begingroup \ $
默认的初始容量为16,但是您可以在调用StringBuilder的构造函数时将其设置为所需的任何值。说StringBuilder将动态调整其缓冲区大小就等于说StringBuilder将分配新的内存。
\ $ \ endgroup \ $
–迈克·纳基斯(Mike Nakis)
15年1月17日在8:17
评论
只要它不是处于非常紧密的循环中,这甚至值得考虑吗?@Bobby-我需要在一个类中构建十几个不同大小的单独字符串,有些需要循环,有些则不需要。不确定是否值得,所以这就是为什么我问:)
不,我的意思是说这有点像过早的优化。如果您开始担心“需要连接的十几个字符串”而没有任何概要分析,那么您的大脑就会打结。我并不是说这是侮辱,而是作为一种温暖的警告……在那里做。经验法则:如果将字符串串联在一起,请使用StringBuilder。第二个经验法则:如果您担心速度,请先配置。
没有采取任何侮辱并指出要点。说实话,我对优化不是太在意,我是一个没有经验的程序员(或者你能说)。我只是想知道这是否会在某种程度上改善性能。鉴于StringBuilders没有.clear()方法,我一半希望有人说,遍历缓冲区以擦除其内容实际上比垃圾回收更昂贵。永远不要问,永远不知道。
没错我认为两者之间的差异不仅很小,而且几乎不明显。我只是想确保您了解这个问题几乎与现实世界中的优化有关