11

Long time ago I watched a video lecture from the Princeton Coursera MOOC: Introduction to algorithms, which can be found here. It explains the cost of resizing an ArrayList like structure while adding or removing the elements from it. It turns out that if we want to supply resizing to our data structure we will go from O(n) to amortized O(n) for add and remove operations.

I have been using Java ArrayList for a couple of years. I've been always sure that they grow and shrink automatically. Only recently, to my great surprise, I was proven wrong in this post. Java ArrayLists do not shrink (even though, of course they do grow) automatically.

Here are my questions:

  1. In my opinion providing shrinking in ArrayLists does not make any harm as the performance is already amortized O(n). Why did Java creators did not include this feature into the design?

  2. I know that other data structures like HashMaps also do not shrink automatically. Is there any other data structure in Java which is build on top of arrays that supports automatic shrinking?

  3. What are the tendencies in other languages? How does automatic shrinking look like in case of lists, dictionaries, maps, sets in Python/C# etc. If they go in the opposite direction to what Java does, then my question is: why?

9
  • 6
    The downside to having your containers shrink is that you will have to grow it again if you add more elements. Just because you removed elements from your list the library can't assume you don't still need the space. I'm not aware of an array wrapper like ArrayList that automatically shrinks, and I would be very surprised to find one doing that. If you as an ArrayList user know that your list capacity can now be reduced, you could use trimToSize(). Commented Jan 30, 2017 at 10:35
  • I understand that there is more work to be done, but in O terms, you lose "nothing" if you provide automatic shrinking, like presented in the video. The complexity for add and remove are still amortized O(n) Commented Jan 30, 2017 at 10:40
  • btw, I would love to read a constructive feedback if someone gives -1. Commented Jan 30, 2017 at 10:41
  • 6
    Automatic growing is required to fit more elements. Automatic shrinking is not necessary and can be done manually if needed as khelwood said. Saying "you lose nothing" is not completely true. You add complexity, and you can't be sure that the automatic shrinking is wanted when it happens. Commented Jan 30, 2017 at 10:44
  • 1
    Unless the JDK developers judged predictability in ArrayList ops preferable to occasional hiccups or custom assembly code in System.arrayCopy being so blazingly fast that it leaves auto-resize in dust - I can't say I see benefit in a) paying guaranteed O(N) each time we invoke remove() and b) burdening the client with manually trimming the list if/when needed, the latter being especially obnoxious. It definitely feels like a mistake. Venturing a guess, I'd say they went with a), though honestly, if you're that concerned with perfomance, you should be using arrays directly anyway. Commented Mar 15, 2017 at 23:19

3 Answers 3

4

The comments already cover most of what you are asking. Here some thoughts on your questions:

  1. When creating a structure like the ArrayList in Java, the developers make certain decisions regarding runtime / performance. They obviously decided to exclude shrinking from the “normal” operations to avoid the additional runtime, which is needed.

  2. The question is why you would want to shrink automatically. The ArrayList does not grow that much (the factor is about 1.5; newCapacity = oldCapacity + (oldCapacity >> 1), to be exact). Maybe you also insert in the middle and not just append at the end. Then a LinkedList (which is not based on an array -> no shrinking needed) might be better. It really depends on your use case. If you think you really need everything an ArrayList does, but it has to shrink when removing elements (I doubt you really need this), just extend ArrayList and override the methods. But be careful! If you shrink at every removal, you are back at O(n).

  3. The C# List and the C++ vector behave the same concerning shrinking a list at removal of elements. But the factors of automatic growing vary. Even some Java-implementations use different factors.

Sign up to request clarification or add additional context in comments.

1 Comment

Because of element size, caching and memory layout, ArrayList outperforms LinkedList in all casual use cases. Never use LinkedList if you don't know exactly what you're doing. dev.to/digitalcrafting/…
0

Another issue with automatic shrinking is that you could get into really horrible 'pathological' situations where each insert and delete to the list causes the growing or shrinking the backing array.

For example, if the backing array's initial capacity is 10, such that the array would grow upon the insertion of the 11th element (capacity would grow to 15), a natural implementation would be to shrink the backing array once the list size dropped below 11. If you had a list whose length kept changing between 10 and 11, you'd be constantly changing the backing array. Not only would that add a runtime overhead, but you could start putting lots of pressure on the garbage collector if every operation resulted in either 10 or 15 objects becoming garbage.

3 Comments

Robert Sedgewick (professor from the course I mention) actually gives a solution to that problem. In your particular case you could increase the size when the 11th element is added to the array. It would be shrink only when there where (for instance) less than 6 elements in the array. This would not worsen the overall big O complexity. When I asked my question I assumed such techniques should be implemented.
@GA1 - First, while it is important to classify algorithms according to their big O complexity (and, in the general case, choose the one with the smallest such complexity), just because two algorithms have the same big O complexity doesn't mean that they have the equivalent run time. Also run time isn't the only consideration (space, or memory usage, is another critical one). So, an array list which grew the array at 11 (to capacity 15) and shrunk it at 6 (to capacity 10), could quickly exhaust all the memory in the system if it was used as a stack between 6 and 11 elements.
@GA1 - With a bit of humor, two people paid hourly both earn O(n) where n is the number of hours they work, but I'd rather get paid 100.00 / hour than 5.00.
0

Although arraylist with shrinking is still amortized O(n) time complexity, it involves more operation.

By shrinking you just save some memory space by adding some calculation, which is not a wise decision because the Moore's law says computer space double every 2 years. So the time is more valuable in algorithm than space.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.