ArrayList初始容量和IndexOutOfBoundsException

考虑这个示例代码:

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添加string“Hello”。这会引发IndexOutOfBoundsException:

线程“main”中的exceptionjava.lang.IndexOutOfBoundsException:索引:5,大小:0

我从一个ArrayList的angular度来看待“初始容量”的含义。 我明白,这个特殊的构造函数正在为7个String元素分配空间,如果我们试图将8个元素添加到列表中,它将不得不分配更多的空间。

明白为什么它不会创build一个大小为7的“空”列表,每个索引都有空值,类似于如果我们声明String[] myArray = new String[7]会发生什么。 我记得学习ArrayList是Java的dynamic数组的实现,所以我期望类似的行为。 如果当我声明new ArrayList<String>(7) ,实际上没有空间来分配7个string,那么究竟发生了什么?

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

这在某些情况下是有用的,而在其他情况下是无用的。 通常你有一个你要创build的列表大小的上限 (或至less是一个猜测),但是然后你填充它…而你不想有一个列表,然后有错误的大小。 ..所以你必须保持一个索引,而你“设置”的价值观,然后再设置大小。

我记得学习ArrayList是Java的dynamic数组的实现,所以我期望类似的行为。

不,它真的不是。 这是一个可以resize的列表,并在幕后使用一个数组。 尽量不要把它看作一个数组。

如果当我声明新的ArrayList<String>(7) ,实际上没有空间来分配7个string,那么究竟发生了什么?

你有7个string引用的空间。 缓冲区大小(即容量)至less为7,但是列表的逻辑大小仍然为0 – 您没有添加任何内容。 就像你有一张足够长的7张纸,但是你还没写任何东西。

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

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

数组的初始容量与其大小 (即数组所包含的元素数量)之间是有区别的。 它的大小是用来确定你是否试图访问一个超出边界的索引。

这是检查的ArrayList.java方法:

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

正如你所看到的,它与arrays的初始容量没有任何关系。 它完全基于它包含的元素的数量。

最初的容量只有一件事:它提供了一个build议(不是要求)的背衬数组应该是多大。 从逻辑上讲,允许或不允许这些build议的操作与build议是一致的。 唯一的变化将是什么内部操作可能发生或不发生作为优化build议。

您只能像那样“添加”到数组中已经存在的位置。 位置5之前的元素还不存在,所以会引发exception。 从Javadoc :

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