26

考虑这个示例代码:

List<String> myList = new ArrayList<String>(7);
myList.add(5, "Hello");
myList.removeAll(Collections.singleton(null));

System.out.println(myList.size() + " objects:" );
for (String s : myList) {
    System.out.println("\t" + s);
}

myList使用初始容量 7 进行初始化,然后下一行尝试在位置 5 处添加字符串“Hello”。这将引发 IndexOutOfBoundsException:

线程“主”java.lang.IndexOutOfBoundsException 中的异常:索引:5,大小:0

我查看了这个关于“初始容量”在 ArrayList 方面的含义的问题。我知道这个特定的构造函数正在为 7 个 String 元素分配空间,如果我们尝试将 8 个元素添加到列表中,它将不得不分配更多空间。

明白的是为什么它没有创建一个大小为 7 的“空”列表,每个索引都有空值,类似于我们声明String[] myArray = new String[7]. 我记得了解到 ArrayList 是 Java 对动态数组的实现,所以我期待类似的行为。如果我在声明时实际上没有分配 7 个字符串的空间new ArrayList<String>(7),那么实际发生了什么?

4

3 回答 3

22

我不明白的是为什么它没有创建一个大小为 7 的“空”列表,每个索引都有空值,类似于我们声明 String[] myArray = new String[7] 时会发生的情况。

这在某些情况下会很有用......而在其他情况下则没有用。很多时候,你有一个你要创建的列表大小的上限(或者至少是一个猜测),但是你填充它......并且你希望有一个大小错误的列表。 ..所以你必须在“设置”值时维护一个索引,然后再设置大小。

我记得了解到 ArrayList 是 Java 对动态数组的实现,所以我期待类似的行为。

不,真的不是。这是一个可以调整大小并在幕后使用数组的列表。尽量不要将其视为一个数组。

如果我在声明 new 时实际上没有分配 7 个字符串的空间ArrayList<String>(7),那么实际发生了什么?

确实有 7 个字符串引用的空间。缓冲区大小(即容量)至少为 7,但列表的逻辑大小仍为 0 - 您还没有向其中添加任何内容。这就像你有一张足够长 7 行的纸,但你还没有写任何东西。

如果你想要一个预填充列表,你可以很容易地编写一个方法来创建一个:

public static List<T> createPrefilledList(int size, T item) {
    ArrayList<T> list = new ArrayList<T>(size);
    for (int i = 0; i < size; i++) {
        list.add(item);
    }
    return list;
}
于 2012-08-10T19:31:36.997 回答
5

数组的初始容量与其大小(即数组包含的元素数量)之间存在差异。它的大小用于确定您是否尝试访问超出范围的索引。

这是ArrayList.java执行此检查的方法:

 private void rangeCheckForAdd(int index) {
   if (index < 0 || index > this.size)
     throw new IndexOutOfBoundsException(outOfBoundsMsg(index));
 }

如您所见,它与阵列的初始容量无关。它仅基于它包含的元素数量。

于 2012-08-10T19:34:51.877 回答
2

初始容量只做一件事:它给出了后备阵列应该有多大的建议(不是要求)。从逻辑上讲,在有或没有建议的情况下允许哪些操作或建议是什么之间没有区别。唯一的变化将是作为优化建议可能发生或不发生的内部操作。

您只能像这样“添加”到数组中已经存在的位置。位置 5 之前的元素尚不存在,因此会引发异常。从Javadoc

抛出:IndexOutOfBoundsException - 如果索引超出范围 (index < 0 || index > size())

于 2012-08-10T19:32:21.260 回答