我在一个循环中使用了一个StringBuilder,每x次迭代我都想清空它,从一个空的StringBuilder开始,但是我看不到任何类似于.NET StringBuilder的方法。在文档中,只需删除看起来过于复杂的方法。
那么,在Java中清除EDCOX1 0的最好方法是什么?
两种工作方式:
使用stringBuilderObj.setLength(0)。
用new StringBuilder()分配一个新的,而不是清除缓冲区。
- 不要太担心性能,除非它是一个巨大的差异,只要我这样做的方式不会让未来的开发人员想知道为什么我这样做一定程度上我很高兴。
- 如果您担心未来的开发人员不理解,您可以发表一些评论。
- @HO1:另外,我认为重新分配比使用两个索引的删除方法更清楚,但这可能是一个优先事项。
- @KRTEK:是的,但是我更喜欢尽可能以一种大多数开发人员甚至不必考虑的标准方式来做,所以不需要评论。
- @马库斯:是的,我想我会同意的,谢谢。
- 不,没那么便宜!你怎么能这么说?假设您有一个容量为1000个字符的缓冲区。然后您处置它(为GC工作)并创建一个新的(为分配器工作)。只需将文本长度设置为零(实际上CPU不工作)并重用相同的缓冲区,速度就快得多。
- @Sulthan:哦,迟了回答:我在考虑StringBuffer.delete(idx,len)。另一方面,做一个SETHOST要求它遍历整个缓冲区,并对每个字符(例如KKJava.COM/SRC/Java/Lange/ActudioStrigBuudio.java.HTM)进行空。根据缓冲区的大小,这也可能很昂贵。另一方面,除非它是超级性能的代码,否则使用对您来说最清晰的代码,不要花时间在微观优化上。
- @马库斯,在您作为示例提供的链接中,setlength(0)不会像您所说的那样迭代,只有当新的长度大于所使用的字符数时才会这样做(长度为0时不会发生)。对于性能而言,setlength(0)似乎是最好的,而且它似乎也是清空缓冲区的一个非常明确的含义。
- @马库斯,你应该更新你的答案。
- 这并不总是可能的,例如,将StringBuffer作为方法的参数。setLength(0)是前进的道路。
- @苏丹是对的。为了清除StringBuilder,我们必须重复完整的长度,并为每个项设置空值。这是一个Java源代码的链接。GRIPCOD.COM/文件/存储库.GRIPCOD.COM/Java/Roo/JDK/Open‌和8203;JDK//Helip;
- @cafebabe1991仔细阅读源代码:if (count < newLength),但如果newLength为0,则不会发生这种情况。
- @苏丹,我有点明白了,但现在你证实了我的想法。
- @Sulthan您是绝对正确的——我有一对分别管理和表示统计信息的类,并且在输出期间需要使用许多格式化的值动态构造冗长的自定义字符串。重复使用同一个StringBuilder,而不是在每个统计打印输出处重新分配,将打印时间缩短了250毫秒!
- 已在Android上确认。setLength(0)的速度是new StringBuilder(200)的两倍。使用no-arg构造函数没有任何区别,可能是因为我只需要构建短字符串。
- 有人能解释为什么使用长度(0)比重新分配更快吗?
- setlength也会导致内存泄漏,但您会发现这样做太晚了。所以人们有时会给出非常愚蠢的答案。setlength除了将长度设置为零之外什么都不做。剩下的分配仍然存在。这个答案源于数组的javascript length=0,它执行了一个神奇的操作来标记数组的可重用性,但即使在那里我也不确定,也不信任它。
- 只是setlegnth(0)创建新StringBuilder的建议很差。如果StringBuilder变大了(比如说100MB),那么当清除缓冲区的目的是重新使用它时,没有必要为了一次又一次地重新创建它而放弃它。
- 这对派对来说有点晚了,不过-@momo setlength没有内存泄漏任何东西。它重用同一个缓冲区,该缓冲区可能大于或不小于您的精确需求,但这是性能行为的预期、正确和最佳缓冲区。当StringBuilder被回收时,内存将被正确地回收,所以这绝不能被称为泄漏。如果在任何时候需要物理地回收内存,只需调用StringBuilder.TrimToSize()即可回收所有不必要的内存,但这与此问题是正交的。
- @杰拉西莫斯的事情是,如果你用一个小的替换一个大的。对sb的引用仍然存在,我怀疑整个数组的旧数组分配将继续存在,不正确的使用可能导致"内存泄漏"。如果重用一个sb alot,并最终重置它并设置一个最终变量,因为您希望重用它,那么所有文本加载的最大大小(无论是通过迭代和打开文件进行分析)都将使sb分配加载的最大文件。我不确定如果某人仍在参考中,GC是否会收回这一点。
- 由于存在内存风险,所以我更喜欢另一种解决方案,它可能没有那么好的性能,但至少始终是安全的。
- @MOMO当然——如果您对"大"字符串使用StringBuilder,那么您将期望为它分配一个"大"数组。如果将长度设置为0,则通常不会期望取消分配数组-您希望实现将该数组重新用于以下使用StringBuilder。这是(几乎?)的预期行为。所有连续分配的容器,如arraylist。我的观点是,这不能也不应该与内存泄漏混淆,这是一个非常具体的事情(一个无法回收的悬空内存块)。
- 也就是说,如果您的意思是"如果您对大字符串使用StringBuilder,然后将其用于小字符串,那么您将使用比小字符串需要的内存更多的内存",是的-但是一般来说,内存分配是非常昂贵的操作,所以大多数人(应该!)无论如何都要这样做,除非在一个非常紧张的内存预算。如果您的意思是,在用户使用完该内存之后(即释放对StringBuilder的所有引用),该内存将以某种方式泄漏,并且不可回收,那么不-该内存在StringBuilder的整个生命周期内都是"正在使用"的。
- @Gerasimosr大型数组内存分配确实是不可调整的,因为用于其他事情的正在使用的StringBuilder仍在起诉该数组。你说这是意料之中的事,但不会拖得太久。如果您要编写一个库或API,其中提供了一个名为reset的函数,那么您不会期望有其他潜在的后果。我说,从图书馆的立场来看,这不是"正确"的做法。如果你知道自己在做什么,如果你是个笨蛋,事情可能会失控。
- 以一个方法声明并返回sb的示例为例。sb可以在内部自由地使用和重置,而不是返回一个新的StringBuilder。实现者决定清除声明的用于读取文件并作为一个较小的StringBuilder返回他需要的任何内容。我有一个重置方法,它不依赖于setlength,因为可能会导致潜在的"内存"泄漏,即由于引用的不是数组空间,而是引用过时的数组空间的sb而无法释放未使用的数组空间。
- 我理解你的意见。也就是说,这不是一个内存泄漏,它是所有三个姐妹语言(C,C,+,Java)中每一个相邻容器共享的有据可查的行为,其中一个STD::vector,和ARARYLIST,一个StrugBu建器和所有其他的有机增长和连续分配的,在改变长度时不会重新分配。这不能称为内存泄漏,而且它有很好的文档记录,并且通过同时提供size/length()和capacity()可以在代码中完全访问。最重要的是,trimToSize函数()确保在需要时回收它
基本上有两种选择,使用setLength(0)重置StringBuilder或在每个迭代中创建一个新的。根据使用情况,两者都有优缺点。
如果您事先知道了StringBuilder的预期容量,那么每次创建一个新的容量应该和设置一个新的长度一样快。它还将帮助垃圾收集器,因为每个StringBuilder都是相对较短的生命周期,并且GC为此进行了优化。
当您不知道容量时,重用同一个StringBuilder可能会更快。每次追加时超过容量时,都必须分配新的备份数组,并且必须复制以前的内容。通过重用同一个StringBuilder,它将在一些迭代之后达到所需的容量,之后将不会有任何复制。
- 谢谢,我忘记了容量参数的构造函数。
- 如果使用setlength(0),这是否意味着它将内部缓冲区保持在当前长度?我担心的是,我不想使用新的StringBuffer,因为我希望有时会有相当长的字符串,因此我从相当大的缓冲区(4K或32K)开始。所以,听起来设置长度(0)可能更快。但是-如果StringBuffer分配的空间永远不会缩小,我可能会耗尽内存(这是在Android下,内存会变得很紧)。
- @迈克尔:是的,内部缓冲区保持在当前长度。您可以在android.googlesource.com/platform/libcore/+/master/luni/src/&zwnj;&8203;&hellip;上找到Android的实际实现。一旦完成字符附加,就可以使用trimToSize方法释放不需要的空间。
- 你写道:"根据用法,两者都有优缺点。"你能举例说明在每次迭代中创建新的StringBuilder更好吗?
- @ICZA的一个例子是,如果您想要并行处理。
- 新的StringBuilder具有更好的可维护性。重用一个变量意味着它将分布在整个代码中。一般来说,变量应该被定位到尽可能紧的范围内,这使得代码更简单、更容易理解。这听起来可能很抽象,但当你在编码一些东西时,它很重要,这些东西将被很多人长期阅读和维护。
delete并不太复杂:
1
| myStringBuilder.delete(0, myStringBuilder.length()); |
您还可以执行以下操作:
1
| myStringBuilder.setLength(0); |
- 复杂可能是个错误的词,我的意思是它看起来不那么整洁。
- 但与执行新的分配相比效率不高。
- 这就是为什么我添加了setlength(0)版本,它应该更快。但新的分配可能会更快。
- setLength的替代方案很有趣,谢谢。
- 考虑将StringBuilder对象作为输出参数传递给函数,则新的分配不是一个选项。
- 所以setlength更有效?
- @mubasharahmad不能在参数中使用new?例如,function(new StringBuilder() ),那么每个函数调用都会创建一个新的分配,不是吗?
如果查看StringBuilder或StringBuffer的源代码,setLength()调用只会重置字符数组的索引值。使用setlength方法的imho总是比新的分配更快。他们应该将该方法命名为"clear"或"reset",以便更清晰。
- @弗兰克哈珀:只有当你在延长绳子的时候。如果你缩小范围,贾瓦曼是对的。
- @你错了。当newLength为零时,源不执行任何操作。
- 同样,setlength也会导致内存泄漏,但您会发现这样做太晚了。所以人们有时会给出非常愚蠢的答案。setlength除了将长度设置为零之外什么都不做。剩下的分配仍然存在。这个答案源于数组的javascript length=0,它执行了一个神奇的操作来标记数组的可重用性,但即使在那里我也不确定,也不信任它。底层数组永远不会被垃圾收集。
我将投票支持sb.setLength(0);,不仅因为它是一个函数调用,而且因为它实际上没有将数组复制到像sb.delete(0, builder.length());这样的另一个数组中。它只需将其余字符填充为0,并将长度变量设置为新的长度。
您可以查看它们的实现,从这里验证我在setLength函数和delete0函数的观点。
- 如果你想投票,然后投票。如果你想要我,然后我。这不是真的在回答。C:
- 不要选择的单词。答案我只是读点湖。
- setlength所以导致内存泄漏,但你会发现太晚出单的方式。setlength是没有比其他的长度设置为零。在这个国家仍然是那里。
- "momomo好的事情是,你可以创建一个新的阵列复用它没有你这样的努力和不必要的,当你在做踢和GC使用StringBuilder的将所有的垃圾收集室。
您应该使用sb.delete(0, sb.length())或sb.setLength(0),而不是创建新的stringbuilder()。
有关性能,请参阅这篇相关文章:在循环中重用StringBuilder更好吗?
1 2 3 4 5 6
| StringBuilder s = new StringBuilder ();
s. append("a");
s. append("a");
// System.out.print(s); is return"aa"
s. delete(0, s. length());
System. out. print(s. length()); // is return 0 |
是个简单的方法。
- 你为什么认为这是最好的方法?对我来说,它看起来比setlength(0)变量更丑。
- 删除调用允许您从StringBuilder对象中删除子字符串;而setLength(0)或setLength(n)只允许您修改StringBuilder对象的容量。换句话说,这两种方法对于完全删除都很有效,但是delete()有更多的功能。
我认为这里的许多答案可能缺少StringBuilder中包含的质量方法:.delete(int start, [int] end)。我知道这是一个迟来的答复;然而,这应该被告知(并解释得更彻底一些)。
假设您有一个StringBuilder表——您希望在整个程序中动态地修改它(我现在正在处理的一个表会这样做),例如。
1
| StringBuilder table = new StringBuilder(); |
如果您正在循环使用该方法并更改内容,请使用该内容,然后希望放弃该内容以"清理"下一个迭代的StringBuilder,您可以删除它的内容,例如。
1
| table.delete(int start, int end). |
开始和结束是要删除的字符的索引。不知道字符长度,想删除整个内容?
1
| table.delete(0, table.length()); |
现在,对于踢球员来说。如前所述,当频繁更改时,StringBuilders会产生大量开销(并可能导致线程方面的安全问题);因此,如果您的StringBuilder用于与用户接口,则使用StringBuffer与StringBuilder相同(少数例外)。
- 想知道这篇文章的投票结果如何吗?
- 从docs.oracle.com JavaSE / 7 /文档/ / / / / stringbuffer.htm Java API和zwnj长";与# 8203;L为JDK 5释放,这类已被设计为一个等价类的补充使用的单线程,StringBuilder。这应该是使用StringBuilder类一般偏好这一,它支持所有的业务,但它是一样快,因为它没有同步执行的。"换句话说,你是正确的关于错误的线程,但对它的性能。
- drojf谢谢@!要更新很快。
如果性能是主要问题…反讽(IMO)是Java构建格式化进入缓冲区的文本将比CPU /重排序/垃圾收集在CPU上耗费更多的时间。嗯,可能不是GC,这取决于您创建和丢弃了多少构建器。
但是简单地将一个复合字符串("hello world of"+6e9+"earthlings.")添加到缓冲区可能会使整个事情变得无关紧要。
而且,实际上,如果StringBuilder涉及的内容复杂而冗长,比简单的EDCOX1(4)要长(无论如何,Java可能在后台使用生成器)。
就我个人而言,我尽量不滥用GC。所以,如果这是一个将在快速射击场景中大量使用的东西,比如说,编写调试输出消息…我只是假设在其他地方声明它,然后将其归零以便重用。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
| class MyLogger {
StringBuilder strBldr = new StringBuilder (256);
public LogMsg ( String stuff ) {
strBldr. setLength(0);
// ... prepend status level
strBldr. append("Info");
// ... prepend timestamp
strBldr. append("" + getTimestamp ());
// ... user msg
strBldr. append(":" + msg );
log. write(strBldr. toString());
}
} |
- 只有在不介意实例大小永远不会缩小的情况下才使用。
- 您是通过operator+连接字符串来向我们展示什么,还是仅仅是代码味道?
- @Mauhiz-strBldr.trimToSize();将在设置长度后释放任何未使用的空间。不幸的是,如果经常使用对象,您只会导致内存混乱,因此如果确实使用对象,最好在.setLength(0)之前而不是之后使用它。