-
继承与实现接口:
ArrayList<E> extends AbstractList<E> implements List<E>, RandomAccess, Cloneable, java.io.Serializable
Vector<E> extends AbstractList<E> implements List<E>, RandomAccess, Cloneable, java.io.Serializable
LinkedList<E> extends AbstractSequentialList<E> implements List<E>, Deque<E>, Cloneable, java.io.Serializable
从上可以看出,ArrayList与Vector都继承了AbstractList并实现了List等接口,而LinkedList继承了AbstractSequentialList,实现了List与Deque接口,再深究AbstractSequentialList同样继承了AbstractList,所有他们三个有一定的共通性,当然ArrayList也Vector更接近。而LinkedList独有的实现Deque接口,为它带来了一些链表所有的void addFirst(E e)
,void addLast(E e)
等方法。
-
添加元素:
- ArrayList:可插入空数据,相当于动态数据,其中最重要的两个属性分别是: elementData 数组,以及 size 大小。
在ArrayList添加数据时,当长度不够需要进行扩容时,都需要将保存数据数组进行一次复制,以及在指定位置插入数据时。//调用 add() 方法 public boolean add(E e) { //首先进行扩容校验 ensureCapacityInternal(size + 1); //将新插入的值放在数组的尾部,并且size加1 elementData[size++] = e; return true; } //调用 add(index,e) 在指定位置添加 public void add(int index, E element) { //校验插入数据的index是否合法 rangeCheckForAdd(index); //进行扩容校验 ensureCapacityInternal(size + 1); //对数据进行复制,目的是把 index 位置空出来放本次插入的数据,并将后面的数据向后移动一个位置 System.arraycopy(elementData, index, elementData, index + 1, size - index); elementData[index] = element; size++; } //扩容校验最终的调用 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); }
- Vector
Vector插入数据的方式与ArrayList是一样的,与ArrayList的区别在于,Vector插入数据是同步执行的。//调用 add() 方法 public synchronized boolean add(E e) { modCount++; ensureCapacityHelper(elementCount + 1); elementData[elementCount++] = e; return true; } //调用 add(index,e) 在指定位置添加 public void add(int index, E element) { insertElementAt(element, index); } public synchronized void insertElementAt(E obj, int index) { modCount++; if (index > elementCount) { throw new ArrayIndexOutOfBoundsException(index + " > " + elementCount); } ensureCapacityHelper(elementCount + 1); System.arraycopy(elementData, index, elementData, index + 1, elementCount - index); elementData[index] = obj; elementCount++; }
- LinkedList: 底层是基于双向链表实现的
每次插入数据都只是移动指针。//插入数据 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++; }
- ArrayList:可插入空数据,相当于动态数据,其中最重要的两个属性分别是: elementData 数组,以及 size 大小。
总结:从上可以看出,在使用ArrayList和Vector时,我们尽量指定长度已经减少指定index插入方式,减少扩容操作,由于他两保存数据的是数值,取数据的效率很高,而且Vector的插入数据是同步执行的,所以我们可以认为Vector是线程安全的,LinkedList插入数据不像ArrayList与Vector在进行扩容时,需要复制整个保存数据的数组,所以效率高,难道LinkedList就十分完美了么?NO、NO、NO,我们来看看LinkedList的查询:
public E get(int index) {
//进行下标验证
checkElementIndex(index);
return node(index).item;
}
Node<E> node(int index) {
// assert isElementIndex(index);
//利用了双向链表的特性,如果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;
}
}
当LinkedList存入的数据够大,而要取靠近中间的值时,迭代数据取值的效率较之另两个就有点低了,所以LinkedList 插入,删除都是移动指针效率很高,但是查找需要进行遍历查询,效率较低。由于LinkedList保存了第一个元素和最后一个元素的值,所以取这两个数据的效率还是很好的。