673

I'm using a StringBuilder in a loop and every x iterations I want to empty it and start with an empty StringBuilder, but I can't see any method similar to the .NET StringBuilder.Clear in the documentation, just the delete method which seems overly complicated.

So what is the best way to clean out a StringBuilder in Java?

Peter Mortensen
  • 28,342
  • 21
  • 95
  • 123
Hans Olsson
  • 51,774
  • 14
  • 88
  • 111

9 Answers9

837

Two ways that work:

  1. Use stringBuilderObj.setLength(0).
  2. Allocate a new one with new StringBuilder() instead of clearing the buffer. Note that for performance-critical code paths, this approach can be significantly slower than the setLength-based approach (since a new object with a new buffer needs to be allocated, the old object becomes eligible for GC etc).
Per Lundberg
  • 2,931
  • 1
  • 29
  • 39
Marcus Frödin
  • 11,134
  • 2
  • 23
  • 16
  • Not too worried about performance unless it's a huge difference so as long as I'm doing it in a way that won't have future developers wonder why I did it in a certain way I'm happy. – Hans Olsson Mar 04 '11 at 10:35
  • 12
    You can leave a little comment if you're afraid future developers won't understand – krtek Mar 04 '11 at 10:36
  • @ho1: Also, I think reallocating is clearer than using the delete method with the two indices, but that's probably a matter of preference. – Marcus Frödin Mar 04 '11 at 10:37
  • @Krtek: Yes, but I prefer when possible to do it in a standard way that most developers won't even have to think about and so no comments will be needed. – Hans Olsson Mar 04 '11 at 10:38
  • 325
    No, it isn't as cheap! How can you say that? Suppose you have a buffer with capacity of 1000 chars. Then you dispose of it (work for GC) and create a new one (work for allocator). It's a lot faster just to set the text length to zero (virtually no work for CPU) and reuse the same buffer. – Sulthan Jun 14 '11 at 09:08
  • 14
    @Sulthan: Oh, late with this answer: I was thinking about StringBuffer.delete(idx, len). On the other hand, doing a setLength requires it to iterate the entire buffer and null each character (e.g. http://kickjava.com/src/java/lang/AbstractStringBuilder.java.htm). Depending on the size of the buffer, that could be expensive as well. On the other hand, unless it's uber-performant code, go with what looks clearest to you and don't spend time on micro-optimization. – Marcus Frödin Jan 02 '12 at 14:34
  • 89
    @Marcus, in the link you provided as an example, setLength(0) will not iterate as you say, it'll do that only if the new length is greater than the used-char count (can't happen with 0 length). For performance it would seems like setLength(0) is the best, and it also seems like a very clear meaning of emptying the buffer. – Eran May 12 '12 at 22:08
  • 20
    @Marcus You should update your answer. – Brian Gordon Jul 16 '13 at 23:11
  • This isn't always possible, e.g. with StringBuffer given as a parameter to a method. `setLength(0)` is the way to go. – Danstahr Aug 06 '13 at 11:53
  • @Sulthan is right. We have to iterate the complete length and null every item in order to clear the StringBuilder. Heres a link of the java source code. http://grepcode.com/file/repository.grepcode.com/java/root/jdk/openjdk/6-b14/java/lang/AbstractStringBuilder.java#AbstractStringBuilder.setLength%28int%29 – cafebabe1991 Jun 03 '14 at 06:50
  • 7
    @cafebabe1991 Read the source code carefully: `if (count < newLength)`, but that will never happen if `newLength` is 0. – biziclop Nov 25 '14 at 15:41
  • @Sulthan I kind of figured it out but now you confirmed my thoughts. – Iharob Al Asimi Aug 17 '15 at 04:03
  • @Sulthan You are definitely correct--I have a pair of classes that respectively manage and represent statistics, and during output need to dynamically construct lengthy custom strings with many formatted values. Reusing the same StringBuilder instead of reallocating at each stat printout decreased print time by 250ms! – EntangledLoops Oct 09 '15 at 14:32
  • 2
    Confirmed on Android. `setLength(0)` is double as fast as `new StringBuilder(200)`. Using no-arg constructor doesn't make any difference, probably because I only need short strings to be built. – sulai Jan 19 '16 at 16:30
  • Can anybody explain why it's faster using length(0) than reallocating? – Asiri Liyana Arachchi Jul 09 '16 at 10:26
  • 1
    just setLegnth(0) The suggestion to create a new StringBuilder is poor. If the StringBuilder gets large (say 100MB) there is no point in discarding it in favor to recreate it over and over again, when the intention of clearing a buffer is to re-use it. – ErezK Apr 27 '17 at 14:01
  • This is a bit late to the party, still - @momo SetLength does not memory leak anything. It reuses the same buffer, which may or may not be bigger or smaller than you would precisely need, but that is the expected, correct and optimal for performance behaviour. The memory will be correctly reclaimed when the StringBuilder is reclaimed, so this can in no way be called a leak. If, at any point, you need to physically reclaim the memory, just call stringBuilder.trimToSize() to reclaim all unnecessary memory, but this is orthogonal to this issue. – Gerasimos R Dec 04 '18 at 12:11
  • @GerasimosR thing is if you replace a large sb with a tiny one .. a reference to the sb will still be there, and I suspect the old array allocation for the entire array will continue to be there, improper usage might lead to "memory leaks". If you reuse an sb alot and in the end reset it and set a final variable, because you want to reuse it, then the largest size of all your text loading (wether it is by iterating and opening files to analyze them) will make your sb allocate the largest file loaded. I am not sure if the gc will reclaim that still if the sb is still in reference. – mmm Dec 04 '18 at 14:54
  • since there is this risk of memory risks, i would prefer another solution which might not be as performant, but at least is always safe. – mmm Dec 04 '18 at 14:55
  • 1
    @momo Sure - if you use a StringBuilder for a "large" string, you would expect to have a "large" array allocated with it. If you set the length to 0, you would not normally expect the array to be deallocated - you would expect the implementation to reuse that array for following uses of the StringBuilder. This is the expected behaviour for (almost?) all contiguously allocated containers, such as ArrayList. My point was that this cannot and should not be confused with a memory leak, which is a very specific thing (a dangling piece of memory that cannot be reclaimed). – Gerasimos R Dec 04 '18 at 17:27
  • I.e. if you mean that "if you use a StringBuilder for a Large string, and then you keep it around using it for small strings, you will be using more memory than the small strings would need", yes - but in general memory allocations are very expensive operations, so most people would (should!) do it anyway unless on a very tight memory budget. If you mean that this memory will somehow be leaked and be unclaimable after the user is finished with it (i.e releases all references to StringBuilder), no - the memory is just "in use" for the lifetime of the StringBuilder. – Gerasimos R Dec 04 '18 at 17:31
  • @GerasimosR The large array memory allocation IS indeed unclaimable due to the array still being in sue by an in use StringBuilder that is used for other things. You saying that it is expected does not hold up. If you were to write a library or an API where you provided a function called reset you would expect no additional potential consequences. I saying that it's not the "correct" approach from a library position. It might work well if you know what you are doing, if you are a noob then things might get out of hand. – mmm Dec 05 '18 at 10:17
  • Take the example where a method declares and returns an sb. The sb could be used and reset freely inside and rather than return a new StringBuilder the implementor decides to clear the one declared which was used to read a file and return whatever he needs as a smaller StringBuilder. I have a reset method for this, and it does not rely on setLength because of the potential "memory" leaks that might cause, namely unused array space that are prevented from being freed due to a reference not to the array space, but to the SB referencing the outdated array space. – mmm Dec 05 '18 at 10:20
  • 3
    I understand your opinion. That said, this is not a memory leak and it is a well documented behaviour shared by every single contiguous container in all three sister languages (C#, C++, Java), where a std::vector, and ArrayList, a StringBuilder and everything else that is organically growing and contiguously allocated will not reallocate when changing length. This cannot be called a memory leak and it is well documented, and perfectly accessible in code by giving you both size/length() and capacity(). Most importantly, the trimToSize function() to ensure it is reclaimed if that's what you want – Gerasimos R Dec 05 '18 at 12:40
  • Some people seem to be posting links to Java source code to show how much work setLength(0) has to do but that work is all bypassed when the new length is 0. That larger chunk of work is only required when the requested length is larger than the current buffer - which will NEVER happen when calling the method with length = 0. Clearly setLength(0) is the most efficient and yes this does make a difference when in million iteration loops. Avoiding instantiating 1 million unnecessary StringBuilders and forcing the GC to clean up that mess is not "unnecessary pre-optimization" in my book. – Volksman Sep 22 '20 at 02:37
  • @Sulthan and others: thanks for the feedback. I know this is an old answer but I've edited it to include a bit more about the performance implications, to at least make people think about more about the performance implications of the code they're writing. – Per Lundberg Apr 13 '21 at 08:10
307

There are basically two alternatives, using setLength(0) to reset the StringBuilder or creating a new one in each iteration. Both can have pros and cons depending on the usage.

If you know the expected capacity of the StringBuilder beforehand, creating a new one each time should be just as fast as setting a new length. It will also help the garbage collector, since each StringBuilder will be relatively short-lived and the gc is optimized for that.

When you don't know the capacity, reusing the same StringBuilder might be faster. Each time you exceed the capacity when appending, a new backing array has to be allocated and the previous content has to be copied. By reusing the same StringBuilder, it will reach the needed capacity after some iterations and there won't be any copying thereafter.

Jörn Horstmann
  • 31,936
  • 11
  • 65
  • 111
  • 1
    Thanks, I had forgotten about the constructor with the capacity parameter. – Hans Olsson Mar 04 '11 at 11:43
  • If you use setLength(0), does that mean it keeps the internal buffer at its current length? My concern is that I don't want to `new` a new StringBuffer because I expect *sometimes* I will have fairly long strings, and thus I am starting with a pretty large buffer size (4k or 32k). So, it sounds like it might be quicker to setLength(0). BUT - if the space allocated by StringBuffer never shrinks, I could run out of memory (this is under Android where memory can get tight). – Michael Mar 30 '13 at 23:50
  • 1
    @Michael: Yes, the internal buffer is kept at its current length. you can find the actual implementation for android at https://android.googlesource.com/platform/libcore/+/master/luni/src/main/java/java/lang/AbstractStringBuilder.java . Once you are finished appending characters you could use the `trimToSize` method to free unneeded space. – Jörn Horstmann Mar 31 '13 at 11:28
  • You wrote: "Both can have pros and cons depending on the usage." Can you give me examples when it is better to create new `StringBuilder` in each iteration? – icza Aug 04 '14 at 10:19
  • 1
    @icza An example is if you want to parallelise the processing. – biziclop Nov 25 '14 at 15:49
  • new StringBuilder has an advantage of better maintainability. Reusing a variable means it will be spread all over the code. Variables in general should be localized to the tightest possible scopes, this makes for a simpler and easier to understand code. This may sound pretty abstract, but it matters when you're coding something that's gonna be read and maintained by a lot of people for a prolonged time. – Vsevolod Golovanov Dec 23 '17 at 18:55
72

delete is not overly complicated :

myStringBuilder.delete(0, myStringBuilder.length());

You can also do :

myStringBuilder.setLength(0);
krtek
  • 25,218
  • 5
  • 53
  • 79
32

If you look at the source code for a StringBuilder or StringBuffer the setLength() call just resets an index value for the character array. IMHO using the setLength method will always be faster than a new allocation. They should have named the method 'clear' or 'reset' so it would be clearer.

Javamann
  • 2,794
  • 1
  • 23
  • 22
  • 4
    @FrankHarper: only if you are extending the string. If you are shrinking it, Javamann is correct. – adam.r Feb 26 '14 at 22:40
  • @FrankHarper You are wrong. The source does nothing when newLength is zero. – mmm Aug 16 '16 at 19:03
  • Also setLength also leads to memory leaks, but you will find that out way way too late. SO folks can give really stupid answers at times. setLength does nothing other than setting the length to zero. The remaining allocations are still there. This answer stems from the javascript length = 0 for arrays, which performs a magical operation to mark the array reusable, but even there i am not sure, and don't trust it. The underlying array will never be garbage collected. – mmm Aug 16 '16 at 19:04
20

I'll vote for sb.setLength(0); not only because it's one function call, but because it doesn't actually copy the array into another array like sb.delete(0, builder.length());. It just fill the remaining characters to be 0 and set the length variable to the new length.

You can take a look into their implementation to validate my point from here at setLength function and delete0 function.

Peter Mortensen
  • 28,342
  • 21
  • 95
  • 123
Ahmed Hegazy
  • 11,465
  • 5
  • 35
  • 62
  • 3
    Don't pick words. just read the answer to see my point. – Ahmed Hegazy Apr 07 '16 at 12:06
  • 1
    setLength also leads to memory leaks, but you will find that out way way too late. setLength does nothing other than setting the length to zero. The remaining allocations are still there. – mmm Aug 16 '16 at 18:53
  • 1
    @momomo The good thing is that you can reuse it without creating a new array and thus saves you from unnecessary GC kick and when you are done using the StringBuilder it'll be all garbage collected anyway. – Ahmed Hegazy Dec 19 '17 at 13:07
8

You should use sb.delete(0, sb.length()) or sb.setLength(0) and NOT create a new StringBuilder().

See this related post for performance: Is it better to reuse a StringBuilder in a loop?

Peter Mortensen
  • 28,342
  • 21
  • 95
  • 123
Tony Meng
  • 323
  • 3
  • 11
5

I think many of the answers here may be missing a quality method included in StringBuilder: .delete(int start, [int] end). I know this is a late reply; however, this should be made known (and explained a bit more thoroughly).

Let's say you have a StringBuilder table - which you wish to modify, dynamically, throughout your program (one I am working on right now does this), e.g.

StringBuilder table = new StringBuilder();

If you are looping through the method and alter the content, use the content, then wish to discard the content to "clean up" the StringBuilder for the next iteration, you can delete it's contents, e.g.

table.delete(int start, int end). 

start and end being the indices of the chars you wish to remove. Don't know the length in chars and want to delete the whole thing?

table.delete(0, table.length());

NOW, for the kicker. StringBuilders, as mentioned previously, take a lot of overhead when altered frequently (and can cause safety issues with regard to threading); therefore, use StringBuffer - same as StringBuilder (with a few exceptions) - if your StringBuilder is used for the purpose of interfacing with the user.

Thomas
  • 4,827
  • 5
  • 28
  • 60
  • Would love to know what the down vote was for on this post? – Thomas Jul 11 '16 at 14:29
  • 1
    from https://docs.oracle.com/javase/7/docs/api/java/lang/StringBuffer.html "As of release JDK 5, this class has been supplemented with an equivalent class designed for use by a single thread, StringBuilder. The StringBuilder class should generally be used in preference to this one, as it supports all of the same operations but it is faster, as it performs no synchronization." - in other words, you are right about the threading, but wrong about the performance. – drojf Oct 31 '16 at 12:29
  • @drojf thanks! Will update, soon. – Thomas Oct 31 '16 at 14:49
4

If performance is the main concern then the irony, in my opinion, is the Java constructs to format the text that goes into the buffer, will be far more time consuming on the CPU than the allocation/reallocation/garbage collection ... well, possibly not the GC (garbage collection) depending on how many builders you create and discard.

But simply appending a compound string ("Hello World of " + 6E9 + " earthlings.") to the buffer is likely to make the whole matter inconsequential.

And, really, if an instance of StringBuilder is involved, then the content is complex and/or longer than a simple String str = "Hi"; (never mind that Java probably uses a builder in the background anyway).

Personally, I try not to abuse the GC. So if it's something that's going to be used a lot in a rapid fire scenario - like, say, writing debug output messages - I just assume declare it elsewhere and zero it out for reuse.

class MyLogger {
    StringBuilder strBldr = new StringBuilder(256);

    public void logMsg( String stuff, SomeLogWriterClass log ) {

        // zero out strBldr's internal index count, not every
        // index in strBldr's internal buffer
        strBldr.setLength(0);

        // ... append status level
        strBldr.append("Info");

        // ... append ' ' followed by timestamp
        // assuming getTimestamp() returns a String
        strBldr.append(' ').append(getTimestamp());

        // ... append ':' followed by user message
        strBldr.append(':').append(msg);

        log.write(strBldr.toString());
    }
}
Jonny Henly
  • 3,725
  • 4
  • 23
  • 41
DevByStarlight
  • 1,024
  • 12
  • 17
  • Only use if you don't mind the fact the instance size will never shrink. – mauhiz Jul 30 '13 at 05:45
  • 4
    Are you concatenating the strings by operator+ to show us something or is it just code smell? – Vlasec Dec 01 '14 at 13:26
  • 1
    @mauhiz `strBldr.trimToSize();` will free any unused space after setting the length. Unfortunately, you'll just be causing memory churn if the object is used often, so it might be best to use it before, rather than after, `.setLength(0)` if you do use it. – Chinoto Vokro Jan 07 '17 at 02:10
4
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

is the easy way.

Ziem
  • 6,059
  • 7
  • 49
  • 83
javadroid
  • 1,355
  • 1
  • 15
  • 39
  • 9
    Why do you thing this is the best way? To me it looks uglier than the setLength(0) variant. – Vlasec Dec 01 '14 at 13:25
  • 1
    The deletion call allows you to remove a substrings from the StringBuilder Object; whereas, setLength(0) or setLength(n) merely allows you to modify the capacity of the StringBuilder object. In other words, both work well for a complete deletion, but delete() has more functionality. – Aidan Melen Jan 13 '16 at 16:39