为什么 Joshua Bloch 在 Effective Java 中使用 2*size + 1 来调整堆栈大小?
Why did Joshua Bloch use 2*size + 1 for resizing the stack in Effective 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);
?
唯一可能有效的情况是 Stack 的初始大小为 0。但在这种情况下它是一个常量 - DEFAULT_INITIAL_CAPACITY(非零值)。并且没有其他重载的构造函数可以从用户那里获取这个值(或默认为 0)
我将其解释为 peace-of-mind 防御假设的未来错误。的确,如所写,这个 class 的数组容量不会为 0,因此添加 1 并不是绝对必要的,但是一旦添加更多功能,该假设可能会悄悄地失败。
可能的附加功能示例包括来自 java.util.ArrayList
的功能,它有一个可以将容量设置为 0 的 trimToSize()
方法,以及一个允许从(可能为空)初始化数据的构造函数集合,以及一个允许显式将容量设置为 0 的构造函数。您还可以想象一个功能,当它被清空时自动减少此 class 的分配容量。或者也许有人会编辑 DEFAULT_INITIAL_CAPACITY
常量。现在想象一下,capacity-changing 方法被满屏的 javadoc 注释分隔开,并拆分为多个子 class 方法。很容易忘记您应该防止容量变为 0。
如果 ensureCapacity()
取决于 non-zero 的大小,您在修改 class 时始终必须牢记这一假设。添加 +1
是一项 low-cost 更改,可以消除这种担忧。这也是处理边缘情况的简单算术方法的示例。
虽然 he/she 在 his/her 答案中确实有更简单的答案,但对我来说,它似乎比 @Boann 使用的所有措辞更明显。答案很简单,作者希望支持以零大小开始数组...设置 DEFAULT_INITIAL_CAPACITY = 0
。这将导致代码在没有 +1
的情况下崩溃。没有其他方法可以使 elements
数组的大小永远为零,除非它以这种方式开始,这是 +1
.
的唯一原因
正如代码所写,没有理由 +1
。
这是我正在谈论的代码:
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);
?
唯一可能有效的情况是 Stack 的初始大小为 0。但在这种情况下它是一个常量 - DEFAULT_INITIAL_CAPACITY(非零值)。并且没有其他重载的构造函数可以从用户那里获取这个值(或默认为 0)
我将其解释为 peace-of-mind 防御假设的未来错误。的确,如所写,这个 class 的数组容量不会为 0,因此添加 1 并不是绝对必要的,但是一旦添加更多功能,该假设可能会悄悄地失败。
可能的附加功能示例包括来自 java.util.ArrayList
的功能,它有一个可以将容量设置为 0 的 trimToSize()
方法,以及一个允许从(可能为空)初始化数据的构造函数集合,以及一个允许显式将容量设置为 0 的构造函数。您还可以想象一个功能,当它被清空时自动减少此 class 的分配容量。或者也许有人会编辑 DEFAULT_INITIAL_CAPACITY
常量。现在想象一下,capacity-changing 方法被满屏的 javadoc 注释分隔开,并拆分为多个子 class 方法。很容易忘记您应该防止容量变为 0。
如果 ensureCapacity()
取决于 non-zero 的大小,您在修改 class 时始终必须牢记这一假设。添加 +1
是一项 low-cost 更改,可以消除这种担忧。这也是处理边缘情况的简单算术方法的示例。
虽然 he/she 在 his/her 答案中确实有更简单的答案,但对我来说,它似乎比 @Boann 使用的所有措辞更明显。答案很简单,作者希望支持以零大小开始数组...设置 DEFAULT_INITIAL_CAPACITY = 0
。这将导致代码在没有 +1
的情况下崩溃。没有其他方法可以使 elements
数组的大小永远为零,除非它以这种方式开始,这是 +1
.
正如代码所写,没有理由 +1
。