LinkedList虽然和ArrayList都实现了List接口,但两者的底层数据结构截然不同。从类名可以看出,ArrayList底层数据结构是数组,而LinkedList底层数据结构是双向链表。两者数据结构的优劣如下,ArrayList按下标查询元素速度快,但插入元素或者删除元素效率低,因为都设计到元素的大量移动,而LinkedList插入或删除元素速度较快,查询中间元素效率较低,因为查询元素需要从距离指定位置最近的链表头部或尾部开始遍历(头部距离指定位置近,就从头部开始遍历,反之,从尾部开始遍历),直到遍历到指定位置为止。
下面开始进入到LinkedList内部进行分析它的实现。首先看核心数据结构-双向链表,是通过内部类Entry来定义的。
private static class Entry<E> {
E element;
Entry<E> next;
Entry<E> previous;
Entry(E element, Entry<E> next, Entry<E> previous) {
this.element = element;
this.next = next;
this.previous = previous;
}
}
此内部类很简单,总共三个属性,element:存放真正数据;next:指向后继的指针;previous:指向前驱的指针。LinkedList持有了此Entry结构的头元素header,所有的操作都通过header进行操作,另外还有一个整型数值size来记录元素个数。LinkedList属性定义如下:
//傀儡节点,header的element是永远为null的,第一个元素其实是存在了header.next.element中,这个可以从getFirst()方法看出来,header也是不计算在size当中的。
private transient Entry<E> header = new Entry<E>(null, null, null);
private transient int size = 0;
//第一个元素其实是存在了header.next.element中
public E getFirst() {
if (size==0)
throw new NoSuchElementException();
return header.next.element;
}
//最后一个元素就是存在header.previous.element中
public E getLast() {
if (size==0)
throw new NoSuchElementException();
return header.previous.element;
}
//add方法是在链表的尾部添加元素,和addLast(E e)方法作用一样
public boolean add(E e) {
addBefore(e, header);
return true;
}
private Entry<E> addBefore(E e, Entry<E> entry) {
Entry<E> newEntry = new Entry<E>(e, entry, entry.previous);
newEntry.previous.next = newEntry;
newEntry.next.previous = newEntry;
size++;
modCount++;
return newEntry;
}
//获取指定下标处的元素,比较指定的下标距离链表头近还是链表尾近,从距离近的地方开始遍历取值
public E get(int index) {
return entry(index).element;
}
private Entry<E> entry(int index) {
if (index < 0 || index >= size)
throw new IndexOutOfBoundsException("Index: "+index+
", Size: "+size);
Entry<E> e = header;
//通过位运算计算出链表的中间位置,下标小于中间位置,说明从header往后遍历效率高,如果下标大于(等于从两边都可以)中间位置,说明从header往前遍历效率高
if (index < (size >> 1)) {
for (int i = 0; i <= index; i++)
e = e.next;
} else {
for (int i = size; i > index; i--)
e = e.previous;
}
return e;
}
总结下:主要通过LinkedList的几个主要方法来分析了下此类,其他方法也都比较容易理解。LinkedList的使用场景:在中间插入或者移除元素比较多的场景下使用。如果经常需要按照下标获取元素,则使用ArrayList。