ArrayList
构造方法
- ArrayList():默认构造方法,初始化一个长度为0的Object[]
private static final Object[] DEFAULTCAPACITY_EMPTY_ELEMENTDATA = {}; public ArrayList() { this.elementData = DEFAULTCAPACITY_EMPTY_ELEMENTDATA; }
- ArrayList(int initialCapacity):初始化一个指定长度的Object[]
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); } }
- ArrayList(Collection<? extends E> c):将传入的集合转为数组,将该数组赋值到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 { // replace with empty array. this.elementData = EMPTY_ELEMENTDATA; } }
考点1:ArrayList其底层数据结构是数组
考点2:ArrayList默认容量是10
相关API
-
add(E e):添加新的元素到集合中,需要对添加后的长度校验是否溢出,否则需要进行扩容处理;然后将对应的索引设值
public boolean add(E e) { ensureCapacityInternal(size + 1); // Increments modCount!! elementData[size++] = e; return true; }
-
add(int index,E e):将新元素添加到指定位置,同理添加前需要进行容量校验,溢出需要扩容;然后将index至–size的所有元素往后移动一位,最后将index位置元素设值
public void add(int index, E element) { if (index > size || index < 0) throw new IndexOutOfBoundsException(outOfBoundsMsg(index)); ensureCapacityInternal(size + 1); // Increments modCount!! System.arraycopy(elementData, index, elementData, index + 1, size - index); elementData[index] = element; size++; }
-
addAll(Collection<? extends E> c):添加一个集合,先将添加集合转数组,然后对添加后的容量进行校验是否溢出,否则需要扩容;最后通过数组拷贝,移植到elementData数组中
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); size += numNew; return numNew != 0; }
-
addAll(int index,Collection<? extends E> c):同理与add(int,Collection<? extends E>逻辑一致,区别是在数组拷贝时,需要空出要添加的元素数组大小
public boolean addAll(int index, Collection<? extends E> c) { if (index > size || index < 0) throw new IndexOutOfBoundsException(outOfBoundsMsg(index)); Object[] a = c.toArray(); int numNew = a.length; ensureCapacityInternal(size + numNew); // Increments modCount int numMoved = size - index; if (numMoved > 0) System.arraycopy(elementData, index, elementData, index + numNew, numMoved); System.arraycopy(a, 0, elementData, index, numNew); size += numNew; return numNew != 0; }
-
remove(int index):移除指定索引位置上的元素,将index+1至–size的所有元素往前移动一位,然后将–size位置的元素设null
public E remove(int index) { if (index >= size) throw new IndexOutOfBoundsException(outOfBoundsMsg(index)); modCount++; E oldValue = (E) elementData[index]; int numMoved = size - index - 1; if (numMoved > 0) System.arraycopy(elementData, index+1, elementData, index, numMoved); elementData[--size] = null; // clear to let GC do its work return oldValue; }
-
remove(Object o):移除指定元素,先遍历集合,找到该元素对应的索引,然后在执行remove(int index)的逻辑
public boolean remove(Object o) { if (o == null) { for (int index = 0; index < size; index++) if (elementData[index] == null) { fastRemove(index); return true; } } else { for (int index = 0; index < size; index++) if (o.equals(elementData[index])) { fastRemove(index); return true; } } return false; }
考点1:频繁的增删,会对性能造成影响,每次操作都需要对数组进行元素的移动,但是读取是它的优势
考点2:扩容规则:先是对原先的容量>>1(即1/2倍),然后+原来的容量(扩容后的容量则是原先的1.5倍);如果不够,则设置添加元素后的容量大小作为扩容后的数组大小,最大不可超过int最大值
考点3:集合Vector与ArrayList的区别?相同的是二者的底层数据结构都是数组,不同点在于Vector是线程安全的,有synchronized关键字修饰
LinkedList
构造方法
- LinkedList():默认构造方法,什么也没做处理
- LinkedList(Collection<? extends E> c):将添加的集合数组化处理,然后把每个元素包装到Node对象中,将first节点指向第一个Node元素节点,然后依次将当前的Node.next绑定到下一个元素对应的Node对象,最终将last节点指向最后一个元素节点
public LinkedList(Collection<? extends E> c) { this(); addAll(c); }
考点:LinkedList的底层数据结构是一个链表
核心API
- add(E e):添加元素;如果当前集合为null,则将first和last指向添加的元素的Node对象并将其prev节点设置前一个节点对象;如果当前集合存在元素,则将最后一个节点的p.next指向添加的元素节点,last从新指向添加元素节点
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++; }
- add(int index,E e):添加元素到指定位置;根据index遍历集合,找到对应的Node,然后将Node.prev.next节点指向添加元素节点,将添加元素节点的next节点指向Node节点,如果是添加到头尾节点,则需要变更first和last节点的指向
public void add(int index, E element) { checkPositionIndex(index); if (index == size) linkLast(element); else // node(index),就是通过遍历查找角标对应的节点 linkBefore(element, node(index)); }
- addAll(int index,Collection<? extends E> c):找到插入点的位置对应的元素节点Node,然后for循环依次将插入集合的元素节点与Node.prev.next关联,然后将插入集合的最后一个元素节点的next节点指向插入点Node的节点,这样就将集合插入到指定位置的链表中
public boolean addAll(int index, Collection<? extends E> c) { checkPositionIndex(index); Object[] a = c.toArray(); int numNew = a.length; if (numNew == 0) return false; Node<E> pred, succ; if (index == size) { succ = null; pred = last; } else { // 找到目标index对应的节点 succ = node(index); pred = succ.prev; } // for循环将集合元素节点依次链到目标位置上 for (Object o : a) { @SuppressWarnings("unchecked") E e = (E) o; Node<E> newNode = new Node<>(pred, e, null); if (pred == null) first = newNode; else pred.next = newNode; pred = newNode; } if (succ == null) { last = pred; } else { // 将插入链表的最后一个节点的next节点指向index对应节点 pred.next = succ; succ.prev = pred; } size += numNew; modCount++; return true; }
- addAll(Collection<? extends E> c):同理addAll(index,c)原理一致,只不过index为size
public boolean addAll(Collection<? extends E> c) { return addAll(size, c); } ```
- remove(Object o):移除指定元素;遍历集合,找出目标元素对应的节点Node,将Node.prev.next指向Node.next节点,然后把Node相关引用置空
public boolean remove(Object o) { if (o == null) { for (Node<E> x = first; x != null; x = x.next) { if (x.item == null) { // 找到对应节点,然后进行断链、重链、清空节点 unlink(x); return true; } } } else { for (Node<E> x = first; x != null; x = x.next) { if (o.equals(x.item)) { // 找到对应节点,然后进行断链、重链、清空节点 unlink(x); return true; } } } return false; }
- remove(int index):移除指定位置上的元素;先通过index找到对应的元素节点,然后同理进行断链、重链、清空
考点1:在频繁的查找中,会影响效率,因为每次操作,都需要进行遍历链表,找到目标所在的节点Node
考点2:在链表查找中,使用的是类似二分法,这样可减少一半的循环次数
考点3:只有在增删在首尾节点时,效率较高,当链表过长时,插入点在1/2处时,效率依旧过慢,因为需要查找一半的元素节点
总结
不同点
- ArrayList底层是数组结构;LinkedList底层是链表结构
- ArrayList存在容量,即有扩容算法;LinkedList无此概念
- ArrayList访问元素速度快,增删慢;LinkedList特殊位置下增删快,查找慢
相同点
- 二者都是线程不安全
- 都是有序的