Java集合之ArrayList / Vector / LinkedList
Java集合之ArrayList /Vector / LinkedList
- ArrayList
- Vector
- LinkedList
- 对比
Java集合中List接口的实现类主要有ArrayList/Vector/LinkedList。本篇博客主要记录三者的常用用法及区别。
ArrayList
ArrayList可以理解为长度可变的数组Array,JVM为其分配连续的内存空间,ArrayList可以进行动态的扩容,默认初始化容量为10(默认空的构造函数new ArrayList()),也可以new ArrayList(int initialCapacity)自定义初始化容量。
/** * Default initial capacity. */ private static final int DEFAULT_CAPACITY = 10;
ArrayList方法
boolean add()
添加一个元素到list尾部,其首先会进行扩容校验,扩容检验过程中会通过if (minCapacity - elementData.length > 0) grow(minCapacity);判断当前list是否能够拥有足够的容量容纳新添加的元素,如果不能容量则执行grow()方法进行扩容,可见,新的容量增加原容量的一半,并且每进行一次扩容就会有一次数据的拷贝。
/** * Appends the specified element to the end of this list. * * @param e element to be appended to this list * @return <tt>true</tt> (as specified by {@link Collection#add}) */ public boolean add(E e) { ensureCapacityInternal(size + 1); // Increments modCount!! 扩容校验 elementData[size++] = e; //然后将元素e添加到list最后位置 return true; }
/** * Increases the capacity to ensure that it can hold at least the * number of elements specified by the minimum capacity argument. * * @param minCapacity the desired minimum capacity */ private void grow(int minCapacity) { // overflow-conscious code int oldCapacity = elementData.length; int newCapacity = 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); }
void add(int index, E element)
添加一个元素到list指定的位置,同样,首先进行扩容校验,然后复制移动数据。从添加元素的两个方法可以看出,ArrayList的数据扩容和指定位置添加数据都会进行数组的复制,比较耗时,因此在使用过程中,尽可能避免扩容操作,也就是初始化ArrayList尽可能指定准确的大小。
/** * Inserts the specified element at the specified position in this * list. Shifts the element currently at that position (if any) and * any subsequent elements to the right (adds one to their indices). * * @param index index at which the specified element is to be inserted * @param element element to be inserted * @throws IndexOutOfBoundsException {@inheritDoc} */ public void add(int index, E element) { rangeCheckForAdd(index); ensureCapacityInternal(size + 1); // Increments modCount!! System.arraycopy(elementData, index, elementData, index + 1, size - index);//复制数据,并将index处的元素后移 elementData[index] = element; size++; }
E get(int index)
返回list指定位置的数据元素,ArrayList本身就是一个数组,数据都是保存在一个Object[] elementData数组的buffer中,因此只需要直接返回elementData[index]即可,可见,ArrayList的查找还是比较高效的(相对LinkedList而言)。
/** * Returns the element at the specified position in this list. * * @param index index of the element to return * @return the element at the specified position in this list * @throws IndexOutOfBoundsException {@inheritDoc} */ public E get(int index) { rangeCheck(index); return elementData(index); }
Vector
Vector是List接口的一个多线程安全实现类,其结构与ArrayList非常相似,同样是一个线性的动态可扩容数组,不过其扩容机制与ArrayList有一定的差别。
Vector方法
boolean add(E e) / void addElement(E obj)
两个方法都是向Vector尾部添加一个元素,先进行扩容校验,然后把元素添加到末尾,从grow()函数可以看出,此时多了一个变量capacityIncrement(扩容量),这个扩容量可以通过构造函数 public Vector(int initialCapacity, int capacityIncrement)进行初始化,如果有初始化的扩容量,则每次扩容增加一个单位的扩容量,否则直接扩容至原数据容量的2倍。
/** * Appends the specified element to the end of this Vector. * * @param e element to be appended to this Vector * @return {@code true} (as specified by {@link Collection#add}) * @since 1.2 */ public synchronized boolean add(E e) { modCount++; ensureCapacityHelper(elementCount + 1); //扩容校验 elementData[elementCount++] = e; //赋值至末尾 return true; }
/** * Adds the specified component to the end of this vector, * increasing its size by one. The capacity of this vector is * increased if its size becomes greater than its capacity. * * <p>This method is identical in functionality to the * {@link #add(Object) add(E)} * method (which is part of the {@link List} interface). * * @param obj the component to be added */ public synchronized void addElement(E obj) { modCount++; ensureCapacityHelper(elementCount + 1); //扩容校验 elementData[elementCount++] = obj; //赋值至末尾 }
private void grow(int minCapacity) { // overflow-conscious code int oldCapacity = elementData.length; int newCapacity = oldCapacity + ((capacityIncrement > 0) ? capacityIncrement : oldCapacity); if (newCapacity - minCapacity < 0) newCapacity = minCapacity; if (newCapacity - MAX_ARRAY_SIZE > 0) newCapacity = hugeCapacity(minCapacity); elementData = Arrays.copyOf(elementData, newCapacity); }
E get(int index) / void add(int index, E element)
基本与ArrayList保持一致
/** * Returns the element at the specified position in this Vector. * * @param index index of the element to return * @return object at the specified index * @throws ArrayIndexOutOfBoundsException if the index is out of range * ({@code index < 0 || index >= size()}) * @since 1.2 */ public synchronized E get(int index) { if (index >= elementCount) throw new ArrayIndexOutOfBoundsException(index); return elementData(index); }
Vector相关的方法基本都添加了synchronized关键字,从而保证了多线程下的线程安全性,但是在单线程下,Vector却与ArrayList的性能有一定的差距。
LinkedList

从图中可以看出,LinkedList同样实现了List接口,因此其实现了List接口里面的所有方法。实际上,LinkedList是基于双向链表来实现的,因此其拥有链表具有的增加和删除元素的效率高的优点,这一点基本上和顺序表为结构基础的ArrayList和Vector集合相对立。
LinkedList方法
boolean add(E e) / void add(int index, E element)
首先看下LinkedList源码部分定义的Node节点,其就是一个双向链表的结构。
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; } }
/** * Appends the specified element to the end of this list. * * <p>This method is equivalent to {@link #addLast}. * * @param e element to be appended to this list * @return {@code true} (as specified by {@link Collection#add}) */ public boolean add(E e) { linkLast(e); //将元素e链接到链表末尾 return true; } /** * Links e as last element. */ void linkLast(E e) { final Node<E> l = last; //临时保存末尾节点 final Node<E> newNode = new Node<>(l, e, null); //构造元素值为e的新节点 last = newNode; //新节点为尾节点 if (l == null) first = newNode; else l.next = newNode; //.next连接 size++; //容量+1 modCount++; //修改数标志+1 }
/** * Inserts the specified element at the specified position in this list. * Shifts the element currently at that position (if any) and any * subsequent elements to the right (adds one to their indices). * * @param index index at which the specified element is to be inserted * @param element element to be inserted * @throws IndexOutOfBoundsException {@inheritDoc} */ public void add(int index, E element) { checkPositionIndex(index); if (index == size) linkLast(element); //判断如果index末尾位置,则直接将元素e链接到链表末尾 else linkBefore(element, node(index)); //否则取出索引处节点(node(index)),然后将索引处的pred.next指向新节点 }
/** * Inserts element e before non-null Node succ. */ void linkBefore(E e, Node<E> succ) { // assert succ != null; final Node<E> pred = succ.prev; //临时保存索引处的前向节点 final Node<E> newNode = new Node<>(pred, e, succ); //初始化元素为e的新节点 succ.prev = newNode; //将索引节点与新节点链接 if (pred == null) first = newNode; else pred.next = newNode; //将临时保存的前向节点链接到新节点 size++; modCount++; }
整体过程可以大致表示如下,其实就是双向链表的插入过程。

此外LinkedList还实现了Deque接口,也实现了Deque里addFirst(E e) / addLast(E e) / offerFirst(E e) / offerLast(E e) / removeFirst() / removeLast() / pollFirst() / pollLast() / peekFirst() / peekLast()等方法。
Java LinkedList.get() 获取元素
对比
三者都实现了List接口,是数据结构中线性表结构的Java实现方式,ArrayList和Vector是顺序表结构,其在内存中地址分配是连续的,而LinkedList是链式存储结构,其在内存分配的地址是不连续的。ArraList和Vector相比,ArrayList是Vector的替代版本,现在Vector都很少使用了,Vector因为是添加了同步关键字而具有线程安全的特点,但是sychronized的性能非常的差,使用List接口下的线程安全类更多的会转向Queue或java.util.concurrent.ConcurrentLinkedQueue等。先总结在这,后续再补充。

浙公网安备 33010602011771号