Search code examples
javaarraysarrayliststringbufferamortized-analysis

StringBuffer and amortization


In ArrayList the add operation is amortized operation. So while reading StringBuffer it came to my mind why the StringBuffer is not amortized. Suppose I use append operation on a string buffer object then it should be able to add those many no of characters in it's underlying array implementation. But instead I found in source code that we have

System.arraycopy(chars, 0, value, count, chars.length);

in append operation of string buffer. So my question is can't the StringBuffer be amortized so it can give us less that O(N) complexity?


Solution

  • At the end of the day, you're still moving N references from some memory location A to some other memory location B. However, I'm confident that System.arraycopy does it fast enough such that there's amoritized performance coming from StringBuffer. But, that depends on if you do append or insert.

    Recall that there are two ways that ArrayList can perform add: either with a single object, or by shifting elements from a particular index spot down. Both have differing performances.

    To get this out of the way, (eventually) StringBuffer will make a call to System.arraycopy(). This implementation is actually JVM-dependent (it's a native call), but the likelihood is that it's extremely fast. Outside of that, there's not much that could really slow down the performance of StringBuffer.append(), save for very large non-contiguous memory areas to copy.

    ArrayList.add(E element) will take an amoritized O(1) time, but could change to O(N) due to the possibility that it has to grow the backing array to fit more elements in it; if it doesn't have to do that, though, then insertion time is on the order of O(1) (it's adding elements to the end of the array).

    ArrayList.add(int index, E element) is likely O(1) in the best case, but O(N - index) in the average and worst cases, due to the amount of elements it has to shift down to place E in.

    To sum up:

    • The code for StringBuffer.append() can be seen as amoritized O(N), since it does copy over the array. However, this operation is still quick and is only really dependent on how large the data you're moving around is.

    • The code for StringBuffer.insert() is different, and could easily be an amoritized O(1) in the best case, and O(N) in the worst (since it makes two calls to System.arraycopy()). Inserting elements at a given point means you have to shift everything else down, and that's not a cheap operation, no matter how fast your code is.

    I believe then, depending on the method you use, you do have amoritized performance. You just have to be sure of the operation you're doing to tell what the performance is going to be.