public ArrayList(intinitialCapacity) {super();if (initialCapacity < 0)thrownew IllegalArgumentException("Illegal Capacity: "+initialCapacity);this.elementData = new Object[initialCapacity];}
带有初始容量的构造器。如果入参小于0报参数非法异常;否则新建一个长度为入参的Object数组并赋值给elementData
public ArrayList(Collection<? extends E> c) {elementData = c.toArray();size = elementData.length;// c.toArray might (incorrectly) not return Object[] (see 6260652)if (elementData.getClass() != Object[].class)elementData = Arrays.copyOf(elementData, size, Object[].class);}
入参是一个集合的构造器。先将入参c转变成Object数组赋值给elementData,size变为数组长度;如果赋值后的elementData不是Object数组,也就是说入参不是Object列表,这时候通过调用Arrays.copyOf方法转变为Object数组
public E get(intindex) {rangeCheck(index);return elementData(index);}privatevoid rangeCheck(intindex) {if (index >= size)thrownew IndexOutOfBoundsException(outOfBoundsMsg(index));}
返回指定位置的元素。如果入参大于数组最大索引,报数组越界异常;否则返回指定位置的数组元素
public E set(intindex, E element) {rangeCheck(index);E oldValue = elementData(index);elementData[index] = element;returnoldValue;}
将指定位置元素替换为入参元素。先检查是否数组越界,将指定位置元素替换成新元素并返回旧元素
publicbooleanadd(E e) {ensureCapacityInternal(size + 1); // Increments modCount!!elementData[size++] = e;returntrue;} privatevoidensureCapacityInternal(intminCapacity) {if (elementData == EMPTY_ELEMENTDATA) {minCapacity = Math.max(DEFAULT_CAPACITY, minCapacity);}ensureExplicitCapacity(minCapacity);} privatevoidensureExplicitCapacity(intminCapacity) {modCount++;// overflow-conscious codeif (minCapacity - elementData.length > 0)grow(minCapacity);} privatevoidgrow(intminCapacity) {// overflow-conscious codeintoldCapacity = elementData.lengthintnewCapacity = oldCapacity + (oldCapacity >> 1);if (newCapacity - minCapacity < 0)newCapacity = minCapacity;if (newCapacity - MAX_ARRAY_SIZE > 0)newCapacity = hugeCapacity(minCapacity);// minCapacity is usually close to size, so this is a win:elementData = Arrays.copyOf(elementData, newCapacity);}
该方法是ArrayList中最重要的方法,也是最复杂的方法,功能就是新增元素在内部数组的尾部。中间用到了一些扩容和数组复制的方法也都贴了出了,接下来对其逐步详细分解。首先要保证当前数组容量至少是当前列表元素个数加1,如果不满足要进行扩容,然后在数组中原来列表尾部索引加1的位置填充入参e;扩容方法ensureCapacityInternal中如果当前数组是空(说明是使用默认构造器新建列表,第一次新增元素),最小容量为默认初始容量private static final int DEFAULT_CAPACITY = 10和入参中较大的一个,然后调用ensureExplicitCapacity 保证数组长度至少为minCapacity;ensureExplicitCapacity 方法中如果最小空间需求量大于当前数组长度,调用grow 方法扩容数组长度至少为minCapacity;grow 方法中,先存储数组的旧长度oldCapacity,暂定新长度newCapacity为旧长度的3/2倍,如果新长度没有最小长度需求minCapacity大,将minCapacity赋值给新长度newCapacity,如果此时的新长度比默认最大数组长度MAX_ARRAY_SIZE大,将Integer最大值赋值给新长度newCapacity,然后将旧数组中的元素复制到新长度的数组中(Arrays.copyOf底层调用System.arraycopy方法)
public E remove(intindex) {rangeCheck(index);modCount++;E oldValue = elementData(index);intnumMoved = size - index - 1;if (numMoved > 0)System.arraycopy(elementData, index+1, elementData, index,numMoved);elementData[--size] = null; // clear to let GC do its workreturnoldValue;}
该方法是移除列表中index位置的元素。首先检查是否下标越界;然后modCount加1;接着用oldValue存储该位置的旧元素,numberMoved记录的是数组中从index位置以后所有需要移动的元素个数;如果需要移动元素个数大于0(index不是数组尾部元素),将数组elementData 中的index+1位置以后的numMoved个元素拷贝到elementData 数组中index位置以后numMoved个元素位置,也就是覆盖,效果就是index后的元素向前移动一位,完成删除elementData(index)的效果
publicvoid clear() {modCount++;// clear to let GC do its workfor (inti = 0; i < size; i++)elementData[i] = null;size = 0;}
清空列表中的元素。其实就是遍历elementData数组,将其每一个索引位置指向null,然后把size变成0。注意:for循环上边有段注释// clear to let GC do its work,什么意思呢,其实我们循环中把数组中的元素都指向null,那么原来开辟的数组元素指向的内存空间已经失去引用,GC回收的时候就可以回收这些空间了
public E getFirst() {final Node<E> f = first;if (f == null)thrownew NoSuchElementException();returnf.item;}privatestaticclass Node<E> {E item;Node<E> next;Node<E> prev;Node(Node<E> prev, E element, Node<E> next) {this.item = element;this.next = next;this.prev = prev;}}
获取列表的第一个元素。该方法是Deque中定义,首先获取第一个Node节点,如果为空(暂时没有元素)抛出异常,否则返回第一个元素的值;接着看到Node是LinkedList中的一个私有静态内部类,存储了当前节点的值以及前后节点的指针,可以得知LinkedList的存储结构是链表,大致如下:
public E getLast() {final Node<E> l = last;if (l == null)thrownew NoSuchElementException();returnl.item;}
返回列表中最后一个元素。如果没有元素抛出异常,否则返回最后一个元素的内容
public E removeFirst() {final Node<E> f = first;if (f == null)thrownew NoSuchElementException(); returnunlinkFirst(f);} private E unlinkFirst(Node<E> f) {// assert f == first && f != null;final E element = f.item;final Node<E> next = f.next;f.item = null;f.next = null; // help GCfirst = next;if (next == null)last = null;elsenext.prev = null;size--;modCount++;returnelement;}
移除列表中第一个元素。如果列表为空,报异常;否则调用unlinkFirst方法解除指针指向;unlinkFirst方法中先记录首元素内容element,然后记录下一个元素next,将首节点内容指向null(GC回收),后指针指向改为null,然后把第二个节点next置为first,如果next为null,说明原来列表中只有一个Node节点,把last也置为null,否则把next的前节点置为null,然后把size减1,最后返回旧节点内容element。其实流程大概如下:
这样就完成首节点移除操作
public E removeLast() {final Node<E> l = last;if (l == null)thrownew NoSuchElementException();return unlinkLast(l);}
移除最后一个节点。类似3中的操作,不再做详细分析
publicvoid addFirst(E e) {linkFirst(e);} privatevoidlinkFirst(E e) {final Node<E> f = first;final Node<E> newNode = new Node<>(null, e, f);first = newNode;if (f == null)last = newNode;elsef.prev = newNode;size++;modCount++;}
将给定元素插入到列表第一个位置。linkFirst方法中f暂存首节点first,然后根据入参e新建一个prev指针null,next指针为f的节点,然后把新节点赋值给first,如果f为null(之前没有元素),last指针也指向新节点;否则原来first节点的元素prev指针指向新节点,然后增加size大小。
以上都是LinkedList特有的方法,接下来我们分析一下List接中有的方法
public E get(intindex) {checkElementIndex(index);return node(index).item;} privatevoidcheckElementIndex(intindex) { if (!isElementIndex(index))thrownew IndexOutOfBoundsException(outOfBoundsMsg(index));} privatebooleanisElementIndex(intindex) {returnindex >= 0 && index < size;}Node<E> node(intindex) {// assert isElementIndex(index);if (index < (size >> 1)) {Node<E> x = first;for (inti = 0; i < index; i++)x = x.next;returnx;} else {Node<E> x = last;for (inti = size - 1; i > index; i--)x = x.prev;returnx;}}
get方法是List中定义的获取指定位置元素的方法。首先检查索引是否合法(>=0&&<size);然后调用node方法查找元素,由于是链表结构,无法直接通过index定位元素,如果index < size>>1(index在size前半部分),从开始节点first节点遍历到index位置返回内容;否则(index在size后半部分)从结尾节点last向前遍历到index位置返回内容。
此处可以看出LinkedList对于查找性能不是太好,不像ArrayList直接通过index定位到内存中的元素,由此可知在查询方面(随机访问)性能不如ArrayList