ArrayList的底层是由数组实现,所以所有的操作都是围绕数组展开,要想理解add方法,就得先了解数组的增加,所以我们先实现一个数组的add,数组的添加可以从尾部增加或者其他位置插入,
如果在数组的尾部插入,只需要拿到数组的长度,直接在该索引处赋予元素值,下面自己手动简单实现一个在数组任意位置实现数组元素添加:
public class MyArray { public static void main(String[] args) { /*创建一个数组*/ Object[] objectArray = new Object[10]; for (int i = 0; i < 10; i++) { objectArray[i] = i; } System.out.println("原数组"); for (int i = 0; i < objectArray.length; i++) { System.out.print(objectArray[i] + ","); } System.out.println(); System.out.println("插入后的数组"); Object[] newArray; try { newArray = MyArray.add(objectArray, 3, "hello"); for (int i = 0; i < newArray.length; i++) { System.out.print(newArray[i] + ","); } } catch (Exception e) { e.printStackTrace(); } } /*在一个数组的任意位置插入一个元素*/ public static Object[] add(Object[] objectArray, int index, Object object) throws Exception { if(index < 0 || index - objectArray.length > 0) { throw new Exception("数组索引无效" + index); } Object[] newObjectArray = new Object[objectArray.length]; /*首先将插入位置之前的所有元素拷贝到一个新的数组中*/ for (int i = 0; i < index; i++) { newObjectArray[i] = objectArray[i]; } /*然后将剩下的元素拷贝到插入位置加1后面的位置*/ for (int i = index + 1; i < objectArray.length; i++) { if(!"".equals(objectArray[i])) { newObjectArray[i] = objectArray[i - 1]; } } /*将新增的元素放到插入的位置*/ newObjectArray[index] = object; return newObjectArray; } }
(1) add方法
如果想在数组的任意位置插入元素,我们需要建立一个新的数组,将插入位置前面的元素拷贝到新数组中,将插入位置后面的元素的下标都后移一位,将插入位置腾出,
将需要插入的元素放入该位置,比起上面的demo,ArrayList的实现稍微复杂,在上述的过程中加入了扩容机制,也不会上面一样将一个数组整个复制到另一个新数组中,
而是原来数组插入位置后面的元素按顺序复制到原数组插入位置+1的位置,下面为ArrayList的add方法源码:
public void add(int index, E element) { /*判断插入的索引是否符合ArrayList范围,在0 和 size之间,size是ArrayList实际元素个数,不包括底层数组的null元素*/ rangeCheckForAdd(index); /*扩容机制:判断添加是否需要进行扩容*/ ensureCapacityInternal(size + 1); // Increments modCount!! /*将旧数组拷贝到一个新数组中,参数:被复制的原数组, 被复制数组的第几个元素开始复制, 复制的目标数组, 从目标数组index + 1位置开始粘贴, 复制的元素个数,*/ System.arraycopy(elementData, index, elementData, index + 1, size - index); /*将新元素赋予该下标*/ elementData[index] = element; /*元素个数+1*/ size++; }
(2) addAll()方法
ArrayList的addAll()方法的实现和add()方法实现思路一致,只不过需要移动的元素更多,由于数组结构的特性,导致这样的操作对于数据大的ArrayList的插入操作,
会严重影响代码执行的效率,所以开发中我们应该尽量避免出现对数据元素多的ArrayList频繁add。
public boolean addAll(Collection<? extends E> c) { /*转为对象数组*/ Object[] a = c.toArray(); int numNew = a.length; /*扩容机制:判断是否需要扩容*/ ensureCapacityInternal(size + numNew); // Increments modCount /*拷贝数组,参数:被复制的数组,被复制数组的第几个元素开始复制,复制到目标数组,目标数组粘贴的位置, 复制的个数*/ System.arraycopy(a, 0, elementData, size, numNew); /*数组长度+numNew*/ size += numNew; return numNew != 0; }
public boolean addAll(int index, Collection<? extends E> c) { /*判断索引是否有效*/ rangeCheckForAdd(index); Object[] a = c.toArray(); int numNew = a.length; /*扩容机制:判断是否需要扩容*/ ensureCapacityInternal(size + numNew); // Increments modCount /*需要移动的元素个数*/ int numMoved = size - index; /*如果插入位置不在尾部,则移动原数组index以后的元素,未插入c腾出空间,如果在尾部,与addAll(Collection<? extends E> c)方法相同*/ if (numMoved > 0) System.arraycopy(elementData, index, elementData, index + numNew, numMoved); /*将需要插入的数组插入到腾出空间中*/ System.arraycopy(a, 0, elementData, index, numNew); /*长度+numNew*/ size += numNew; return numNew != 0; }