🔍 基于JDK1.8的ArrayList和LinkedList的源码解析。
⚡ 涉及的API:Arrays#copyOf(U[] original, int newLength, Class<? extends T[]> newType)
用于数组克隆,参数如下:
original:原数组
newLength:新数组长度
newType:新数组类型
克隆时会有以下两种情况:
如果新数组的长度大于原数组,那么多出的部分用null填充。
如果新数组的长度小于原数组,那么多出的部分直接丢弃。
ArrayList 基本知识
始于:JDK 1.2
作者:Josh Bloch , Neal Gafter
实现:List , RandomAccess , Cloneable , Serializable
特点:
可存储任何类型数据(包括null)
可存储重复数据
存储数据保证有序性
非线程安全
类图:
基本属性 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 private static final int DEFAULT_CAPACITY = 10 ;private static final Object[] EMPTY_ELEMENTDATA = {};private static final Object[] DEFAULTCAPACITY_EMPTY_ELEMENTDATA = {};transient Object[] elementData; private int size;private static final int MAX_ARRAY_SIZE = Integer.MAX_VALUE - 8 ;protected transient int modCount = 0 ;
构造方法 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 public ArrayList (int initialCapacity) { if (initialCapacity > 0 ) { this .elementData = new Object [initialCapacity]; } else if (initialCapacity == 0 ) { this .elementData = EMPTY_ELEMENTDATA; } else { throw new IllegalArgumentException ("Illegal Capacity: " + initialCapacity); } } public ArrayList () { this .elementData = DEFAULTCAPACITY_EMPTY_ELEMENTDATA; } public ArrayList (Collection<? extends E> c) { elementData = c.toArray(); if ((size = elementData.length) != 0 ) { if (elementData.getClass() != Object[].class) elementData = Arrays.copyOf(elementData, size, Object[].class); } else { this .elementData = EMPTY_ELEMENTDATA; } }
add方法 用途:向数组末尾添加元素。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 public boolean add (E e) { ensureCapacityInternal(size + 1 ); elementData[size++] = e; return true ; } private void ensureCapacityInternal (int minCapacity) { ensureExplicitCapacity(calculateCapacity(elementData, minCapacity)); } private static int calculateCapacity (Object[] elementData, int minCapacity) { if (elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA) { return Math.max(DEFAULT_CAPACITY, minCapacity); } return minCapacity; } private void ensureExplicitCapacity (int minCapacity) { modCount++; if (minCapacity - elementData.length > 0 ) grow(minCapacity); } private void grow (int minCapacity) { int oldCapacity = elementData.length; int newCapacity = oldCapacity + (oldCapacity >> 1 ); if (newCapacity - minCapacity < 0 ) newCapacity = minCapacity; if (newCapacity - MAX_ARRAY_SIZE > 0 ) newCapacity = hugeCapacity(minCapacity); elementData = Arrays.copyOf(elementData, newCapacity); } private static int hugeCapacity (int minCapacity) { if (minCapacity < 0 ) throw new OutOfMemoryError (); return (minCapacity > MAX_ARRAY_SIZE) ? Integer.MAX_VALUE : MAX_ARRAY_SIZE; }
首先确保容量,所需容量 = size + 1。
对于刚刚创建的ArrayList:所需容量 = max(初始化容量10,当前容量)。
如果所需容量大于数组长度,则进行扩容,默认扩容为1.5倍,不够则给予所需容量;
如果所需容量大于最大容量,那么只能扩容为Integer.MAX_VALUE
。
最后将元素添加到数组尾部。
get方法 用途:获取执行下标的元素。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 public E get (int index) { rangeCheck(index); return elementData(index); } private void rangeCheck (int index) { if (index >= size) throw new IndexOutOfBoundsException (outOfBoundsMsg(index)); } E elementData (int index) { return (E) elementData[index]; }
先进行越界检查,然后直接根据下标返回数组元素。
remove方法 用途:移除指定下标的元素。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 public E remove (int index) { rangeCheck(index); modCount++; E oldValue = elementData(index); int numMoved = size - index - 1 ; if (numMoved > 0 ) System.arraycopy(elementData, index+1 , elementData, index, numMoved); elementData[--size] = null ; return oldValue; }
先进行越界检查,然后将需要删除的元素之后的元素全部向前移动一位,并将数组尾部元素置为null。
trimToSize方法 用途:将数组容量调整为实际元素个数大小,当ArryList元素个数不发生改变时,可以调用该方法减少内存占用。
1 2 3 4 5 6 7 8 9 public void trimToSize () { modCount++; if (size < elementData.length) { elementData = (size == 0 ) ? EMPTY_ELEMENTDATA : Arrays.copyOf(elementData, size); } }
LinkedList 基本知识
基本属性 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 private static class 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; } } transient int size = 0 ;transient Node<E> first;transient Node<E> last;protected transient int modCount = 0 ;
构造方法 1 2 3 4 5 6 7 8 9 public LinkedList () {} public LinkedList (Collection<? extends E> c) { this (); addAll(c); }
add方法 获取指定下标的元素。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 public boolean add (E e) { linkLast(e); return true ; } void linkLast (E e) { final Node<E> l = last; final Node<E> newNode = new Node <>(l, e, null ); last = newNode; if (l == null ) first = newNode; else l.next = newNode; size++; modCount++; }
先获取尾节点(用于判断链表是否为空),创建新节点存放当前元素,并将新节点设置为新的尾节点。
若链表为空,则将新节点设置为新的头节点,否则将新节点连接到尾节点的后面,然后将链表长度和改变次数+1。
get方法 获取指定下标的元素。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 public E get (int index) { checkElementIndex(index); return node(index).item; } private void checkElementIndex (int index) { if (!isElementIndex(index)) throw new IndexOutOfBoundsException (outOfBoundsMsg(index)); } private boolean isElementIndex (int index) { return index >= 0 && index < size; } Node<E> node (int index) { if (index < (size >> 1 )) { Node<E> x = first; for (int i = 0 ; i < index; i++) x = x.next; return x; } else { Node<E> x = last; for (int i = size - 1 ; i > index; i--) x = x.prev; return x; } }
先进行越界检查,然后根据索引与链表长度关系决定是从头开始遍历还是从尾开始遍历。
remove方法 移除指定下标的元素。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 public E remove (int index) { checkElementIndex(index); return unlink(node(index)); } E unlink (Node<E> x) { final E element = x.item; final Node<E> next = x.next; final Node<E> prev = x.prev; if (prev == null ) { first = next; } else { prev.next = next; x.prev = null ; } if (next == null ) { last = prev; } else { next.prev = prev; x.next = null ; } x.item = null ; size--; modCount++; return element; }
先获取待删节点的前驱节点和后继节点,简称为前点和后点。
若前点为空,则直接将头节点设置为后点;否则前点不为空,将前点的后继节点设置为后点,将待删节点与前点断开连接。
若后点为空,则直接将尾节点设置为前点;否则后点不为空,将后点的前驱节点设置为前点,将待删节点与后点断开连接。
最后将待删节点置为null,链表长度-1,并返回删除节点的值。