What is more efficient StringBuffer new() or delete(0, sb.length())?
Asked Answered
K

3

15

It is often argued that avoiding creating objects (especially in loops) is considered good practice.

Then, what is most efficient regarding StringBuffer?

StringBuffer sb = new StringBuffer();
ObjectInputStream ois = ...;

for (int i=0;i<1000;i++) {

    for (j=0;i<10;j++) {
        sb.append(ois.readUTF());
    }
    ...

    // Which option is the most efficient? 
    sb = new StringBuffer(); // new StringBuffer instance?
    sb.delete(0,sb.length()); // or deleting content?

}

I mean, one could argue that creating an object is faster then looping through an array.

Kinser answered 23/8, 2011 at 23:51 Comment(2)
Have you tested it in a profiler? What were the results?Rashidarashidi
See also codereview.stackexchange.com/questions/7575/…Mitziemitzl
C
17

First StringBuffer is thread-safe which will have bad performance compared to StringBuilder. StringBuilder is not thread safe but as a result is faster. Finally, I prefer just setting the length to 0 using setLength.

sb.setLength(0)

This is similar to .delete(...) except you don't really care about the length. Also probably a little faster since it doesn't need to 'delete' anything. Creating a new StringBuilder (or StringBuffer) would be less efficient. Any time you see new Java is creating a new object and placing that on the heap.

Note: After looking at the implementation of .delete and .setLength, .delete sets length = 0, and .setLength sets every thing to '\0' So you may get a little win with .delete

Conformist answered 23/8, 2011 at 23:57 Comment(5)
If the StringBuilder gets a very large internal buffer allocated which is wasted because an early loop iteration produces a very large string, and later iterations don't, then you might end up thrashing longer than necessary, but otherwise keeping the buffer is a net win since if later iterations produce strings almost the same size as earlier iterations, you are avoiding some buffer copies.Soutache
delete is not O(N) when the delete clears the entire contents.Soutache
This is true, I am updating my answer. But only when len == count which is true if you are deleting all.Conformist
setLength will not sets everything to '\0'. Only if the new size is > to the current one and only for the part of the internal buffer that was extended.My
Surprisingly, StringBuilder.delete() is 26% faster than StringBuilder.setLength(0) per JMH. It just goes to show you, you can't determine what's faster simply by looking at the code.Thermograph
C
3

Just to amplify the previous comments:

From looking at source, delete() always calls System.arraycopy(), but if the arguments are (0,count), it will call arraycopy() with a length of zero, which will presumably have no effect. IMHO, this should be optimized out since I bet it's the most common case, but no matter.

With setLength(), on the other hand, the call will increase the StringBuilder's capacity if necessary via a call to ensureCapacityInternal() (another very common case that should have been optimized out IMHO) and then truncates the length as delete() would have done.

Ultimately, both methods just wind up setting count to zero.

Neither call does any iterating in this case. Both make an unnecessary function call. However ensureCapacityInternal() is a very simple private method, which invites the compiler to optimize it nearly out of existence so it's likely that setLength() is slightly more efficient.

I'm extremely skeptical that creating a new instance of StringBuilder could ever be as efficient as simply setting count to zero, but I suppose that the compiler might recognize the pattern involved and convert the repeated instantiations into repeated calls to setLength(0). But at the very best, it would be a wash. And you're depending on the compiler to recognize the case.

Executive summary: setLength(0) is the most efficient. For maximum efficiency, pre-allocate the buffer space in StringBuilder when you create it.

Campanula answered 6/7, 2012 at 1:43 Comment(0)
S
1

The delete method is implemented this way:

public AbstractStringBuilder delete(int start, int end) {
    if (start < 0)
        throw new StringIndexOutOfBoundsException(start);
    if (end > count)
        end = count;
    if (start > end)
        throw new StringIndexOutOfBoundsException();
    int len = end - start;
    if (len > 0) {
        System.arraycopy(value, start+len, value, start, count-end);
        count -= len;
    }
    return this;
}

As you can see it doesn't iterate through the array.

Supper answered 24/8, 2011 at 0:4 Comment(4)
What do you think System.arraycopy does??Conformist
@Mike I am not sure what you mean len will be 0. It is computed from end - start which will sb.length() - 0. What you mean to say count-end will be 0 which is true. But generally you don't compute O(n) for only one set of params.Conformist
@Amir, sorry I think I was right about no significant work being done but I was wrong about why. When start is 0 and end is count aka this.length(), then you end up doing System.arraycopy(value, count, value, 0, 0). The last 0 means it is copying 0 bytes from index count to index 0.Soutache
I've looked at the source code. In this particular case, arraycopy() will do nothing but return. All you've done is waste the overhead of a function call.Campanula

© 2022 - 2024 — McMap. All rights reserved.