总述
LinkedList,应该不会陌生吧,我们先简单看下它的继承体系:
我们从图中可以看到,LinkedList继承的接口中不光有List,还有Queue、Deque这两种数据结构。
说明LinkedList不仅可以作为List,还是双端队列。研究前,先看下实现它的数据结构:
从图中,我们可以知道LinkedList是用链表来实现的。并且还有2个指针专门的指向头和尾。我们可以设想下:
- 如果从last处加入,并且从first处取出,是不是就是队列-先进先出
- 如果从last出加入,并且从last处取出,是不是就是栈-后进先出
详细分析
作为队列
我们可以怎么用?只需要使用Queue接口的方法,如下:
boolean add(E e);
boolean offer(E e);
E remove();
E poll();
E element();
E peek();
3种功能:插入队列尾、移除队列头、查看队列头,为什么需要6个方法呢,是定义接口时候为了考虑不同的情况,当队列头出队时遇到null,remove操作会抛出NoSuchElementException异常,而poll操作返回null,当获取队列头元素时遇到null,element会抛出NoSuchElementException,peek会返回null。由于LinkedList是无界的,由此实现的队列,add和offer添加是一模一样的操作,成功插入。
作为栈
大家熟知的规范接口定义:
public void push(E e)
public E pop();
但是使用pop时需要注意,当元素为空是,会抛出运行时异常:NoSuchElementException;如果不想进行异常处理,考虑poll方法,返回null,这是种折中的方式,毕竟pop方法只能描绘一种情景。
由于LinkedList承担的责任过大,导致方法太多,有时候使人非常的疑惑。个人建议,当用LinkedList作为栈或者队列使用时,不妨用LinkedList作为底层重新进行封装,如同这样
public class Stack<E> {
private LinkedList<E> list = new LinkedList<E>();
public Stack() {
}
public E push(E item) {
list.push(item);
return item;
}
public E pop() {
return list.pop();
}
}
不仅使用起来简单方便,需要什么功能加入什么功能,不会造成接口污染,而且特定的数据结构命名,有时候也能把问题说的更清楚。
作为list集合使用
完全可以类比ArrayList,只不过LinkedList链表的实现,特殊场景下性能可能比arraylist好些吧。但是也得具体情况具体分析,例如:如果全是插入最后,能够预知数据量大小的情况下arraylist的性能也并不会差,只有集合中间频繁加入,删除数据的情况下,LinkedList才会显现出绝对的优势。