ArrayList 的 add(index,object) 方法抛出 IndexOutOfBoundException

ArrayList's add(index,object) method throw IndexOutOfBoundException

关于使用下面提到的代码片段创建 arrayList:

List arrayList = new ArrayList(16);

ArrayList 的内部实现创建一个大小为 16 的数组 elementData 并在每个位置分配 null。在做类似 arrayList.add(2,"HelloWorld") 的事情时给出 IndexOutOfBoundException 作为添加元素的索引(即 2)大于 size arrayList 的属性。

从javaDocs中可以清楚地看出,arrayList的size属性在初始化arrayList时初始化为0,每次递增1 arrayList

添加了一个新元素

谁能解释一下,为什么 ArrayList dataStructure 最初是这样设计的。即使内部数据结构 elementData 在创建 arrayList 时被初始化为 16 个空值,它仍然不允许在 indeces > size 处添加值; (假设在这种情况下索引 <16)。实现 add(index,object) 的想法是什么 由 arrayList 的大小属性控制的功能?

拥有一个大小大于 List.size() 的内部数组的目的是避免不必要地重新分配数组。如果内部数组总是和 List 有相同的大小,那么每次添加新元素时,内部数组都必须重新分配,从而导致性能下降。

您不能在特定索引处添加对象,直到它包含 null。您只需要使用 add 方法添加对象,然后就可以更新索引上的值。

例如

 ArrayList<Integer> arrlist = new ArrayList<Integer>(5);

// use add() method to add elements in the list
arrlist.add(15);
arrlist.add(22);
arrlist.add(30);
arrlist.add(40);

// adding element 25 at third position
arrlist.add(2,25);

您很少需要指定 ArrayList 容量 ,只有当您知道 ArrayList 将容纳多少元素时,它才能提高性能.

ArrayList 只是一个可以自动增大或缩小的 List。使用 List,你 永远不会 需要在 n 的地方添加一个元素,如果列表是空的,你只需 link 它到前一个节点(当然除非它是头) - 这就是 ArrayList 的想法,除了它可以 grown/shrink 自动。

其实ArrayList的默认构造函数构造的是一个初始容量为10的列表

public ArrayList() {
    this(10);
} 

但是为什么我们需要这样的分配呢?正如您所理解的,如果您预先指定 ArrayList 的大小,则可以为列表提供高效。否则,当元素个数超过ArrayList的初始容量后,对每个元素重新进行重新分配操作

documentation 说:

public void add(int index, E element)

Throws:

IndexOutOfBoundsException - if the index is out of range (index < 0 || index > size())

如您所见,如果 (index > size()),它会抛出 IndexOutOfBoundsException。 由于 ArrayList 的 "public int size()" returns 元素不等于 null,因此您的大小等于 0(不是您在示例中所说的 16)。换句话说,如果也计算空值,则使用默认构造函数创建的每个 ArrayList 的大小将为 10.

因此,"arrayList.add(2, "HelloWorld")" 抛出 IndexOutOfBoundsException,因为 index = 2 但 size() = 0。

编辑:

我想当你提出你的论点时,你使用这个作为基础:

String[] arr = new String[5];
arr[3] = "hello";

System.out.println(arr[3]); 

然后,你想为什么你可以直接给数组元素赋值,为什么你不能用ArrayList的add(int index, E element)方法来做同样的事情。实际上,这是真的,但没有条件将 ArrayList 实现为 Array 的完整对应物。换句话说,此方法以该规则为条件,因为它是 ArrayList 的本质。众所周知,当你创建一个数组时,你在方括号中指定它的大小。以 int 作为参数的 ArrayList 的构造函数不做同样的事情。它只执行虚构的分配。是的,它可以通过此分配指定其初始大小,或者在调用 add(int index, E element) 后,大小可以增加一。但是,实现 ArrayList 是为了提供一种类似数组的结构,该结构在索引号方面具有连续性,但没有固定大小。因此,有一些其他更高级别的抽象示例可以完成此任务。举个例子,LinkedHashMap结构。

参见 ArrayList 的 java 文档 add(int index, E元素)方法。在这里您可以找到 ArrayIndexOutOfBound 异常发生在 if the index is out of range (index < 0 || index > size()) 时。

您声明了一个初始容量为 16 的 ArrayList。这并不意味着 ArrayList 的 16 个索引位置中的每一个都包含元素。它只提到初始容量,必要时它会动态增加它的大小。

ArrayListclass-

查看构造函数的源代码
/**
     * Constructs an empty list with the specified initial capacity.
     *
     * @param   initialCapacity   the initial capacity of the list
     * @exception IllegalArgumentException if the specified initial capacity
     *            is negative
     */
    public ArrayList(int initialCapacity) {
    super();
        if (initialCapacity < 0)
            throw new IllegalArgumentException("Illegal Capacity: "+
                                               initialCapacity);
           this.elementData = new Object[initialCapacity];
    }

在这里我们找不到任何告诉我们的信息 - ArrayList 将以空值启动。

更新: 根据您的评论,我做了一些实验,因为我不确定 reference/non-primitive 类型的数组是否会初始化为 null。请参阅下面的代码。 运行 通过在每次执行时取消注释每一行的代码 -

import java.util.List;
import java.util.ArrayList;

public class ArrayListTest{

    public static void main(String[] args){

        List<String> list1 = new ArrayList<String>(); //default with initial capacity 10
        List<String> list2 = new ArrayList<String>(5);
        List<String> list3 = new ArrayList<String>(5);

        list2.add(null);
        list2.add(null);
        list2.add(null);

        list3.add("zero");
        list3.add("one");
        list3.add("two");

        //System.out.println(list1.get(4)); //IndexOutOfBoundException

        //System.out.println(list2.get(0)); //null
        //System.out.println(list2.get(2)); //null;
        //System.out.println(list2.get(3)); //IndexOutOfBoundException

        //System.out.println(list3.get(0)); //zero
        //System.out.println(list3.get(2)); //two;
        //System.out.println(list3.get(3)); //IndexOutOfBoundException
        //list3.add(4, "four"); //IndexOutOfBoundException

    }

}

在这里你可以看到 list2.get(0)list2.get(2) 给你 null。因为我们将 null 放在这些索引处。但是 list2.get(3) 没有给出 null 因为我们没有把 null 放在索引 3 处。所以 reference/non-primitive 类型的数组似乎不会用 [=22= 初始化]. list2.get(3) 给出 IndexOutOfBoundException

您发现 list3. 的情况相同,但我没有在此列表中放置任何 null。即使我们试图在 list3 的索引 4 处添加一些值,它也会给出 IndexOutOfBoundException。由于索引 4 不适用于 list3。但是您可以在 list2 的索引 2 处添加一些值。因为在此列表的索引 2 处,我手动插入了 null

长话短说(我认为)- new ArrayList<SomeType>(givenSize) 不会用 givenSize 初始化数组,所有元素设置为 null

希望对您有所帮助。
谢谢。