ChatGPT解决这个技术问题 Extra ChatGPT

How can I clear or empty a StringBuilder? [duplicate]

This question already has answers here: Clearing a string buffer/builder after loop (10 answers) Closed 6 years ago.

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?


P
Per Lundberg

Two ways that work:

Use stringBuilderObj.setLength(0). 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).


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: 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. 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, 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.
@Marcus You should update your answer.
@cafebabe1991 Read the source code carefully: if (count < newLength), but that will never happen if newLength is 0.
J
Jörn Horstmann

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.


Thanks, I had forgotten about the constructor with the capacity parameter.
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: Yes, the internal buffer is kept at its current length. you can find the actual implementation for android at android.googlesource.com/platform/libcore/+/master/luni/src/… . Once you are finished appending characters you could use the trimToSize method to free unneeded space.
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 An example is if you want to parallelise the processing.
k
krtek

delete is not overly complicated :

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

You can also do :

myStringBuilder.setLength(0);

Complicated is probably the wrong word, I meant more that it doesn't look as neat.
But not overly efficient compared to performing a new allocation.
This is why I added the setLength(0) version, which should be faster. But probably that a new allocation will be faster.
The setLength alternative was interesting, thank you.
consider a stringbuilder object is passed as an output parameter to a function, then new allocations is not an option.
J
Javamann

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.


@FrankHarper: only if you are extending the string. If you are shrinking it, Javamann is correct.
@FrankHarper You are wrong. The source does nothing when newLength is zero.
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.
P
Peter Mortensen

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.


Don't pick words. just read the answer to see my point.
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.
@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.
P
Peter Mortensen

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?


T
Thomas

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.


Would love to know what the down vote was for on this post?
from 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 thanks! Will update, soon.
Z
Ziem
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.


Why do you thing this is the best way? To me it looks uglier than the setLength(0) variant.
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.
J
Jonny Henly

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());
    }
}

Only use if you don't mind the fact the instance size will never shrink.
Are you concatenating the strings by operator+ to show us something or is it just code smell?
@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.