12

This is the code I am talking about:

public class Stack {
  private Object[] elements;
  private int size = 0;
  private static final int DEFAULT_INITIAL_CAPACITY = 16;
  public Stack() {
    elements = new Object[DEFAULT_INITIAL_CAPACITY];
  }
  public void push(Object e) {
    ensureCapacity();
    elements[size++] = e;
  }
  public Object pop() {
    if (size == 0)
      throw new EmptyStackException();
    return elements[--size];
  }
  /**
   * Ensure space for at least one more element, roughly
   * doubling the capacity each time the array needs to grow.
   */
  private void ensureCapacity() {
    if (elements.length == size)
      elements = Arrays.copyOf(elements, 2 * size + 1);
  }
}

Why not simply keep the last line as elements = Arrays.copyOf(elements, 2 * size);?

The only case where it might have been valid would be if the initial size of Stack was 0. But in this case it is a constant - DEFAULT_INITIAL_CAPACITY (a non zero value). And there is no other overloaded constructor which could take this value from user (or default it to 0)

Tintin
  • 2,853
  • 6
  • 42
  • 74
  • 8
    If starting size is 0, just doubling doesn’t work too well. – Nathan Hughes Oct 16 '20 at 18:21
  • yeah but it does not here - it starts with non-zero DEFAULT_INITIAL_CAPACITY (16) here. If it was created from the parameter in the constructor - it might have been a valid case. – Tintin Oct 16 '20 at 18:27
  • 4
    I think the problem is if you had an initial capacity of 0. This isn't possible in the code you've posted, I know, but I suspect this Joshua Bloch guy probably intended to add an overloaded constructor, taking an argument for custom initial capacity. If you passed in 0 to such a constructor, the code would not behave as expected if it only doubled the length. That said, a similar effect could also be achieved using an if statement to check for 0, which, personally, would be my first approach. – Charlie Armstrong Oct 16 '20 at 18:29
  • _this Joshua Bloch guy_ -- Jee, he is Joshua Bloch for heaven's sake! He doesn't write code assuming stuff on readers part and thats why I asked this question -- just to double check if I am not missing out on something subtle here! – Tintin Oct 16 '20 at 18:34
  • This or someone probably subclassed this and added this "reset" method or overloaded the constructor to accept a 0 size. – Tintin Oct 16 '20 at 18:48
  • I think the missing assumption is that you always need to be able to clear out the allocated capacity. Regardless of initial amount you shouldn’t assume it couldn’t have been reset to 0. – Nathan Hughes Oct 16 '20 at 18:51
  • this might have historic reasons that you will never find out (may be something to do with zero as the starting size when `DEFAULT_INITIAL_CAPACITY ` did not exist? or something like this). In `ArrayList::grow`, btw, it's a simple "double the size" via `oldCapacity >> 1`. – Eugene Oct 16 '20 at 20:02
  • There's no way to subclass this code to change the basic behavior. Everything is `private`. You can't do anything with it that couldn't be done by instantiating it directly. – CryptoFool Oct 17 '20 at 01:13

2 Answers2

13

I interpret it as peace-of-mind defense against a hypothetical future bug. It's true that as written, this class won't have an array capacity of 0, so adding 1 is not strictly necessary, but that assumption could quietly fail once more features are added.

Examples of possible additional features include those from java.util.ArrayList, which has a trimToSize() method that can set the capacity to 0, and a constructor which allows initializing the data from a (possibly empty) collection, and a constructor that allows explicitly setting the capacity to 0. You can also imagine a feature that reduces this class's allocated capacity automatically when it is emptied. Or maybe someone will edit the DEFAULT_INITIAL_CAPACITY constant. Now imagine that capacity-changing methods become separated by screenfuls of javadoc comments, and split across subclasses. It's easy to forget you were supposed to prevent the capacity becoming 0.

If ensureCapacity() depends on the size being non-zero, you always have to keep that assumption in mind while you rework the class. Adding +1 is a low-cost change that removes that worry. It's also an example of a simple arithmetic way of dealing with an edge case.

Boann
  • 48,794
  • 16
  • 117
  • 146
1

It seems a bit more obvious to me than all the wording @Boann uses, although the he/she does have the simpler answer inside his/her answer. The answer is simply that the author wants to support starting the array at a size of zero...of setting DEFAULT_INITIAL_CAPACITY = 0. This will cause the code to crash without the +1. There's no other way that the size of the elements array can ever be zero except if it starts out that way, and that's the only reason for the +1.

As the code is written, there's no reason for the +1.

CryptoFool
  • 21,719
  • 5
  • 26
  • 44