Почему Джошуа Блох использовал 2 * size 1 для изменения размера стека в эффективной Java?

#java #effective-java

#java #эффективный-java

Вопрос:

Это код, о котором я говорю:

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

Почему бы просто не сохранить последнюю строку как elements = Arrays.copyOf(elements, 2 * size); ?

Единственный случай, когда это могло бы быть допустимым, это если бы начальный размер стека был равен 0. Но в данном случае это константа — DEFAULT_INITIAL_CAPACITY (ненулевое значение). И нет другого перегруженного конструктора, который мог бы принять это значение от пользователя (или по умолчанию оно равно 0)

Комментарии:

1. Если начальный размер равен 0, простое удвоение работает не слишком хорошо.

2. да, но здесь этого нет — здесь он начинается с ненулевого значения DEFAULT_INITIAL_CAPACITY (16) . Если он был создан из параметра в конструкторе — это могло быть допустимым случаем.

3. Я думаю, проблема в том, что у вас была начальная емкость 0. Я знаю, что в опубликованном вами коде это невозможно, но я подозреваю, что этот парень Джошуа Блох, вероятно, намеревался добавить перегруженный конструктор, принимая аргумент для настраиваемой начальной емкости. Если вы передали 0 такому конструктору, код не будет вести себя так, как ожидалось, если он только удвоит длину. Тем не менее, аналогичный эффект также может быть достигнут с помощью оператора if для проверки 0, что лично было бы моим первым подходом.

4. этот парень Джошуа Блох — Джи, он Джошуа Блох, ради всего святого! Он не пишет код, предполагающий что-то со стороны читателей, и именно поэтому я задал этот вопрос — просто чтобы перепроверить, не упускаю ли я здесь что-то тонкое!

5. Это или кто-то, вероятно, подкласс this и добавил этот метод «сброса» или перегрузил конструктор, чтобы принять размер 0.

Ответ №1:

Я интерпретирую это как защиту от гипотетической будущей ошибки. Это правда, что, как написано, этот класс не будет иметь емкость массива 0, поэтому добавление 1 не является строго необходимым, но это предположение может незаметно потерпеть неудачу при добавлении новых функций.

Примерами возможных дополнительных функций являются функции from java.util.ArrayList , которые имеют trimToSize() метод, который может устанавливать емкость на 0, и конструктор, который позволяет инициализировать данные из (возможно, пустой) коллекции, и конструктор, который позволяет явно устанавливать емкость на 0. Вы также можете представить функцию, которая автоматически уменьшает выделенную емкость этого класса при его освобождении. Или, может быть, кто-то отредактирует DEFAULT_INITIAL_CAPACITY константу. Теперь представьте, что методы, изменяющие емкость, разделяются полными экранами комментариев javadoc и разделяются по подклассам. Легко забыть, что вы должны были предотвратить превращение емкости в 0.

Если ensureCapacity() зависит от ненулевого размера, вы всегда должны учитывать это предположение при переработке класса. Добавление 1 — это недорогое изменение, которое устраняет это беспокойство. Это также пример простого арифметического способа решения пограничного случая.

Ответ №2:

Мне это кажется немного более очевидным, чем все формулировки, которые использует @Boann, хотя у него / нее есть более простой ответ в его / ее ответе. Ответ прост: автор хочет поддерживать запуск массива с нулевым размером … настройки DEFAULT_INITIAL_CAPACITY = 0 . Это приведет к сбою кода без 1 . Нет другого способа, чтобы размер elements массива мог быть равен нулю, кроме как если он начинается таким образом, и это единственная причина для 1 .

По мере написания кода нет причин для 1 .