LinkedList作为队列添加删除数据位置的总结
LinkedList是使用链表实现的List,并且它是双向List。List中的数据通过引用有顺序地排列。ArrayList把数据保存在数组中,而LinkedList的数据保存在Node中,通过Node中的指向其他Node的引用,串成一个List。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;
}
}
LinkedList中有一个头指针first和尾指针last。分别指向List中的第一个元素以及最后一个元素。之所以不说最新加入的元素或者最晚加入的元素,因为可以在LinkedList的头或者尾加入元素。而头指针和尾指针,也是为了不引起操作混乱,所以给了LinkedList以方向性,也就是说做了个规定。
LinkedList中还有个size属性,是int值。这说明,获取LinkedList中元素个数时,是时间复杂度为1的操作。
LinkedList除了具有List的直接添加元素、在指定位置获取或者设置元素的操作外,更重要的是有队列的操作:把数据入队或者出队。LinkedList在队首或者队尾
都可以进行添加元素或者获取元素
的操作。
下面总结一下LinkedList在队首或者队尾所有添加或者移除或者获取的方法:
add系操作
add操作有两个方法add(E e)
和add(int index, E element)
,源码分别是:
public boolean add(E e) {
linkLast(e);
return true;
}
public void add(int index, E element) {
checkPositionIndex(index);
if (index == size)
linkLast(element);
else
linkBefore(element, node(index));
}
add(E e)
是把元素添加到队尾的。add(int index, E element)
不同,它会检查要添加的位置,这里要注意,因为虽然指定了位置,但是它并不是插入到定长数组中,这里看代码最容易理解:如果插入位置和list中元素个数相同,也就是从逻辑上来说,这个位置对于list还不存在,但却是list中下一个元素的位置,那么就加入到队尾;否则,就插入到指定的位置,也就是插入到指定位置的元素之前。
add(E e)
添加后会返回true,而add(int index, E element)
无返回值,且如果位置超出范围,还会抛异常。
offer系操作
offer系操作是从队列角度的操作,是往队列里添加元素。
先把offer的源码贴出来:
public boolean offer(E e) {
return add(e);
}
可以看到,offer实际上就是调用了前面的add(E e)
,也就是说,它是添加到队尾的。
而为了添加到队首,jdk提供了offerFirst(E e)
操作:
public boolean offerFirst(E e) {
addFirst(e);
return true;
}
同样为了明确添加到队首,提供了offerLast操作:
public boolean offerLast(E e) {
addLast(e);
return true;
}
addLast(e)
实际上就是linkLast(e)
,也就是说,offerLast(E e)
和add(E e)
是一样的操作。
这3个方法,都会返回true。
peek系操作
peek系最简单的是peek()
方法:
public E peek() {
final Node<E> f = first;
return (f == null) ? null : f.item;
}
这个方法可以获取到队列的第一个元素,也就是first指向的元素。如果队列是空的,它就会返回null。注意,它不会删除LinkedList中的元素。
peek()是返回队列的第一个元素,但是不够明确,于是有了peekFirst()
方法:
public E peekFirst() {
final Node<E> f = first;
return (f == null) ? null : f.item;
}
代码和peek是一模一样的。
同样为了获取到队列的最后一个元素,有了E peekLast()
:
public E peekLast() {
final Node<E> l = last;
return (l == null) ? null : l.item;
}
同样,如果队列是空的,那么返回null。
这3个方法,都是LinkedList实现了来自Deque接口的方法。看一下jdk文档中peek和peekFirst的定义:
peek
: Retrieves, but does not remove, the head of the queue represented by this deque (in other words, the first element of this deque), or returns null if this deque is empty.This method is equivalent to peekFirst(). return the head of the queue represented by this deque, or null if this deque is emptypeekFirst
: Retrieves, but does not remove, the first element of this deque,or returns null if this deque is empty.
return the head of this deque, or null if this deque is empty
从官方解释来看,peek方法,等价于peekFirst。
poll系操作
poll系也有和peek相似的3个方法:poll(),pollFirst(),pollLast()。
poll的功能也和peek相似,它可以获取到队尾或者队首的元素,但不一样的是,poll会从队列中移除该获取的元素。也就是说,poll具有出队的功能。
public E poll() {
final Node<E> f = first;
return (f == null) ? null : unlinkFirst(f);
}
public E pollFirst() {
final Node<E> f = first;
return (f == null) ? null : unlinkFirst(f);
}
public E pollLast() {
final Node<E> l = last;
return (l == null) ? null : unlinkLast(l);
}
从以上源码可以看出,poll()和pollFirst()获取到的是队首的元素,pollLast()获取的是队尾的元素。
offer和peek,以及poll系列方法,都是队列操作方法,都是来自Deque接口。
remove系操作
LinkedList不管是作为List,还是Deque,它当然有remove方法:
public E remove() {
return removeFirst();
}
remove方法,会移除地一个元素并返回。但它和同样会移除元素的poll系操作的区别是:如果队列为空,那么它会抛异常。从remove()调用的removeFirst()就可以看出来:
public E removeFirst() {
final Node<E> f = first;
if (f == null)
throw new NoSuchElementException();
return unlinkFirst(f);
}
在上面说remove()就已经说了removeFirst(),remove系操作还有removeLast()
,E remove(int index)
,boolean remove(Object o)
,boolean removeFirstOccurrence(Object o)
,boolean removeLastOccurrence(Object o)
:
这些方法中,E removeLast()
,E remove(int index)
和前面remove一样,都是移除元素并返回元素。
public E removeLast() {
final Node<E> l = last;
if (l == null)
throw new NoSuchElementException();
return unlinkLast(l);
}
public E remove(int index) {
checkElementIndex(index);
return unlink(node(index));
}
同样,如果移除的元素不存在,则会抛异常。
而后面3个remove方法,都是以Object对象为参数。它是移除队列中和Object对象相同的元素。boolean remove(Object o)
会移除所有相同的元素,而后面两个则分别只移除第一个或者最后一个。
对于如何判断元素是否相同,分2种情况,如果传入参数是null,则比较元素是否是null,否则调用对象的equals方法进行比较。
pop和push操作
LinkedList还可以作为堆栈使用
public void push(E e) {
addFirst(e);
}
public E pop() {
return removeFirst();
}
很明确地,添加的时候添加到队首,移除的时候从队首移除,就实现了堆栈的功能。