LinkedList浅析

(一)LinkedList的定义

public class LinkedList<E>
    extends AbstractSequentialList<E>
    implements List<E>, Deque<E>, Cloneable, java.io.Serializable

       其中,AbstractSequentialList是AbstractList通过索引访问、删除元素的骨干实现,而Deque定义了一套能从链表头尾访问的接口,使得LinkedList能当做双端队列使用。Deque插入,删除,访问的方法,每种方法都存在两种形式:操作失败时抛出异常、返回特殊值(null或false):

        第一个元素(头部)                 最后一个元素(尾部)
        抛出异常        特殊值            抛出异常          特殊值
插入    addFirst(e)    offerFirst(e)    addLast(e)       offerLast(e)
移除    removeFirst()  pollFirst()      removeLast()     pollLast()
访问    getFirst()     peekFirst()      getLast()        peekLast()

(二)重要属性:

    transient int size = 0;
    transient Node<E> first;
    transient Node<E> last;
    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;
        }
    }

       其中size是链表中元素的大小,first是指向链表头结点的引用,last是指向链表尾结点的引用。Node是链表结点的定义,item是链表的元素,next是指向下一个结点的引用,prev是指向上一个结点的引用。

(二)添加元素,删除元素

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++;
    }

       在添加元素的过程中,先获取指向链表尾部结点的引用last,然后构造一个新结点newNode,newNode的前一个引用指向原链表的尾部,下一个引用为null。接着,将原链表尾部的引用指向新结点,判断原来的尾结点是否为空,为空则将原链表的头引用指向新结点,不为空则将原链表的尾结点的下一个引用指向新结点,元素结点数量+1,结构性修改计数+1。至此,新元素被添加到链表的末尾。

(三)访问元素

LinkedList访问元素:

    public E get(int index) {
        checkElementIndex(index);
        return node(index).item;
    }

检查索引是否越界:

    private void checkElementIndex(int index) {
        if (!isElementIndex(index))
            throw new IndexOutOfBoundsException(outOfBoundsMsg(index));
    }
    
    private boolean isElementIndex(int index) {
        return index >= 0 && index < size;
    }

根据索引获取结点:

    Node<E> node(int index) {
        // assert isElementIndex(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;
        }
    }

       可以看到,首先判断当前索引index是否小于链表中元素大小的一半:index < (size >> 1),小于则从链表头部开始遍历,否则,从链表尾部开始遍历,直至找到目标。也因为此,当LinkedList中元素数量很多时,通过索引访问LinkedList将变得很慢,时间复杂度为O(n)。

(四)LinkedList实现队列,栈

当用LinkedList实现队列时,等效方法分别如下:

实现队列:

队列方法       等效方法
add(e)        addLast(e)
offer(e)      offerLast(e)
remove()      removeFirst()
poll()        pollFirst()
element()     getFirst()
peek()        peekFirst()
/**
 * @Author: weiwenfeng
 * @Date: 2018/11/16
 */
public class Queue<E> {

    private LinkedList<E> linkedList = new LinkedList<E>();

    boolean add(E e){
        linkedList.addLast(e);
        return true;
    }

    boolean offer(E e){
        linkedList.offerLast(e);
        return true;
    }

    E remove(){
        return linkedList.removeFirst();
    }

    E poll(){
        return linkedList.pollFirst();
    }

    E element(){
        return linkedList.getFirst();
    }

    E peek(){
        return linkedList.peekFirst();
    }
}

实现栈:

栈方法        等效方法
push(e)      addFirst(e)
pop()        removeFirst()
peek()       peekFirst()
/**
 * @Author: weiwenfeng
 * @Date: 2018/11/16
 */
public class Stack<E> {

    private LinkedList<E> linkedList = new LinkedList<>();

    E push(E e){
        linkedList.addFirst(e);
        return e;
    }

    E pop(){
        return linkedList.removeFirst();
    }

    E peek(){
        return linkedList.peekFirst();
    }
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值