LinkedList源码分析

LinkedList

LinkedList采用双端链表的数据结构实现,且由于双端链表的特性,LinkedList也可以作为队列、双端队列来使用,不仅如此也可以作为栈使用。

public class LinkedList<E> extends AbstractSequentialList<E> implements List<E>, Deque<E>, Cloneable, Serializable {

(1)实现了List接口,具备List的特性
(2)实现Deque接口,具备双端链表特性
(3)实现Clonable接口,可以被克隆
(4)实现了Serializable,可以被序列化

属性

    //元素个数
    transient int size;
    //头结点
    transient LinkedList.Node<E> first;
    //尾结点
    transient LinkedList.Node<E> last;

	//双链表节点内部类
	private static class Node<E> {
        E item;//数据
        Node<E> next;//next指针
        Node<E> prev;//prev指针

		//初始化构造方法
        Node(Node<E> prev, E element, Node<E> next) {
            this.item = element;
            this.next = next;
            this.prev = prev;
        }
    }

构造方法

    /**
     * 1. 默认构造
     */
	public LinkedList() {
    }

    /**
     * 2. 通过传入一个集合创建链表
     */
    public LinkedList(Collection<? extends E> c) {
        this();
        addAll(c);
    }

头部或末尾添加元素

作为双端链表,可以在链表头部添加节点,也可以在链表尾部添加节点,其时间复杂度都是O(1) ,而作为双端队列时所用到的offerFirst,offerLast都是调用了下面的方法。

    /**
     * 1. 添加元素到链表头部
     */
    private void linkFirst(E e) {
    	//头结点
        final Node<E> f = first;
        //创建newNode.value = e, newNode.next-> 头结点 
        final Node<E> newNode = new Node<>(null, e, f);
        //将newNode置为新的头结点
        first = newNode;
        //判断是否是第一个添加的节点
        if (f == null)
        	//如果是, 则把newNode也置为尾节点
            last = newNode;
        else
        	//否则原来的头节点.prev指向newNode
            f.prev = newNode;
        size++;
        modCount++;
    }
    
    /**
     * 2. 链表尾部添加元素
     */
    void linkLast(E e) {
    	//获取末尾节点
        final Node<E> l = last;
        //创建newNode.value = e, newNode.prev = 尾结点
        final Node<E> newNode = new Node<>(l, e, null);
        //将newNode置为新的尾结点
        last = newNode;
        //若第一次添加节点
        if (l == null)
        	//则把newNode也置为first节点
            first = newNode;
        else
        	//否则原尾结点.next指向newNode
            l.next = newNode;
        size++;
        modCount++;
    }

add(int index, E element) index位置添加元素

由于node(int index)涉及到链表的遍历,因此add(int i, E e)方法时间复杂度是O(n)

    public void add(int index, E element) {
        //判断是否越界
        checkPositionIndex(index);
		//若插入的节点是末尾节点
        if (index == size)
        	//直接调用linkLast
            linkLast(element);
        else
        	//否则先通过index检索到插入节点的后置节点
        	//再调用linkBefore进行插入
            linkBefore(element, node(index));
    }

	/**
     * 通过index检索到目标节点
     */
    Node<E> node(int index) {
    	//若index < size/2, 则从头节点开始遍历
        if (index < (size >> 1)) {
            Node<E> x = first;
            for (int i = 0; i < index; i++)
                x = x.next;
            return x;
        } else {
        //若index >= size/2, 则从尾结点开始遍历
            Node<E> x = last;
            for (int i = size - 1; i > index; i--)
                x = x.prev;
            return x;
        }
    }

	/**
     * 在某个不为null的节点前插入一个新节点
     */
    void linkBefore(E e, Node<E> succ) {
        // 找到待插入节点的前置节点pred
        final Node<E> pred = succ.prev;
        // 创建newNode.prev = 前置节点, newNode.next = 后置节点succ
        final Node<E> newNode = new Node<>(pred, e, succ);
        // 后置节点succ.prev = newNode
        succ.prev = newNode;
        // 若前置节点为空, 说明succ原本是头节点
        if (pred == null)
        	//此时将newNode置为新的头节点
            first = newNode;
        else
        	//否则前序节点pred.next = newNode
            pred.next = newNode;
        size++;
        modCount++;
    }

头部或末尾删除元素

作为双端链表,可以在链表头部删除节点,也可以在链表尾部删除节点,其时间复杂度都是O(1) ,而作为双端队列时所用到的pollFirst,pollLast都是调用了下面的方法。

    /**
     * 删除链表头节点
     */
    private E unlinkFirst(Node<E> f) {
        // 获取当前头节点value
        final E element = f.item;
        // 获取头节点的后置节点next
        final Node<E> next = f.next;
        f.item = null;
        f.next = null; // help GC
        // 后置节点next置为新的头节点
        first = next;
        // 若删除了原头节点链表为空, 证明原头节点也末尾节点
        if (next == null)
        	//那么把末尾节点也置为空
            last = null;
        else
        	//否则后置节点.prev置为空
            next.prev = null;
        size--;
        modCount++;
        //返回删除节点value
        return element;
    }

    /**
     * 删除末尾节点
     */
    private E unlinkLast(Node<E> l) {
        // 获取当前尾节点value
        final E element = l.item;
        // 获取尾节点的前置节点prev
        final Node<E> prev = l.prev;
        l.item = null;
        l.prev = null; // help GC
        // prev置为新的尾结点
        last = prev;
        // 如果prev == null, 说明原尾节点也是头节点
        if (prev == null)
        	//头节点置为空
            first = null;
        else
        	//否则prev.next置为空
            prev.next = null;
        size--;
        modCount++;
        return element;
    }

remove(int index) index位置删除元素

    public E remove(int index) {
        checkElementIndex(index);
        //同样的, 先调用node(int index)检索到指定节点
        return unlink(node(index));
    }

    /**
     * 删除指定节点x
     */
    E unlink(Node<E> x) {
        // x的元素值
        final E element = x.item;
        // x的后置
        final Node<E> next = x.next;
        // x的前置
        final Node<E> prev = x.prev;
		// 如果前置节点为空, 说明x是头节点
        if (prev == null) {
        	//将头节点置为后置节点next
            first = next;
        } else {// 否则修改前置节点prev.next = x的后置
            prev.next = next;
            x.prev = null;//help GC
        }
		
		//如果后置节点为空, 说明x是尾结点
        if (next == null) {
        	//将前置prev置为新的尾节点
            last = prev;
        } else {//否则修改后置节点next.prev = x的前置
            next.prev = prev;
            x.next = null;//help GC
        }

        x.item = null;// help GC
        size--;
        modCount++;
        return element;
    }

总结

(1)LinkedList是以双端链表实现的List
(2)同时具备双端队列,队列,栈的特性
(3)首尾增、删元素非常高效,时间复杂度为O(1)
(4)中间增、删元素时间复杂度为O(n)

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值