数组列表初始容量和索引出界异常

2022-09-01 17:35:45

请考虑以下代码示例:

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:

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

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

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


答案 1

我不明白的是,为什么它没有创建一个大小为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;
}

答案 2

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

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

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

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