LinkedList源码,你想了解一下不?

12 篇文章 0 订阅
2 篇文章 0 订阅

前言:

上回咱们介绍了ArrayList源码,详情见《ArrayList源码,你想了解一下不?》文章,今天来介绍一下LinkedList源码,LinkedList源码也不是很难理解,它的底层用的双链表,如果你对常用的数据结构有所了解,你在学习理解LinkedList源码中关于链表的相关方法就很容易。LinkedList与ArrayList都实现了List接口,如果你原来看过ArrayList源码,你在学习理解LinkedList源码中关于List接口相关方法就很容易。听我这么半天忽悠,你是不是也想学习LinkedList源码。

零、LinkedList的理解:

LinkedList的功能比较强大,它实现了List和Deque两个接口的功能,在使用Deque双向队列功能时,它能模拟队列和栈两种数据结构。它的底层是使用的链表,链表这种数据结构在插入和删除元素时,时间复杂度要优于顺序表;在查询元素时,时间复杂度高于顺序表,但是LinkedList源码也做相关优化,它首先会比较索引位置值与一半size值的大小,小于从frist结点(头结点)遍历,反之从last结点(尾结点)遍历。

一、LinkedList的数据结构:

LinkedList的数据结构是一个双向链表,其中,这里有一个结点的概念,双向列表结点包含前继结点、元素、后继结点三个部分组成;这里也有size的概念,代表是链表中结点的个数:

                                                                  

二、LinkedList源码分析: 

1.成员变量:

   /**
     * 链表中结点数量
     *
     */
    transient int size = 0;

    /**
     * 指向第一个结点的指针(头结点)
     * 不变的: (first == null && last == null) || (first.prev == null && first.item != null)
     */
    transient Node<E> first;

    /**
     * 指向最后一个结点的指针(尾结点)
     * 不变的: (first == null && last == null) ||(last.next == null && last.item != null)
     */
    transient Node<E> last;

2.构造函数:

   /**
     * 构造函数
     */
    public MyLinkedList() {
    }

    /**
     * 构造函数,包含指定的集合,按集合返回它们的顺序迭代器。
     *
     * @param  c 要将其元素放入此列表中的集合
     * @throws NullPointerException 如果指定的集合为空
     */
    public MyLinkedList(Collection<? extends E> c) {
        this();
        addAll(c);
    }

 3.结点的数据结构方法:

 /**
     * 结点数据结构
     *
     * @param <E>
     */
    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;
        }
    }

4.操作链表的相关方法:

(1)E linkFirst(E e) 方法:

   /**
     * Links e 作为第一个元素(头结点)
     *
     * @param e
     */
    private void linkFirst(E e) {
        // 把头结点赋值给 f
        final Node<E> f = first;
        // 创建一个新结点(前继:null,后继:f)
        final Node<E> newNode = new Node<>(null, e, f);
        // 把newNode赋值给first(头结点)
        first = newNode;
        // 如果f等于空,即原头结点为空,又因这个方法只是设置第一个元素(头结点),
        // 所以说明链表为空
        if (f == null) {

            // 尾结点也是新结点(newNode),因为就一个结点
            last = newNode;

        } else {// 如果f不等于空,即原头结点不为空

            // 原头结点的前继结点指向新结点(newNode)
            f.prev = newNode;
        }
        // 结点数量加一
        size++;
        // modCount自增一
        modCount++;
    }

操作链表如下图: 

情况1:头结点为空时,插入newNode结点:

情况2:头结点不为空时,插入newNode结点:

Tips:linkLast(E e)方法与 linkFirst(E e)类似,我就不展示了

(2)E linkBefore(E e, Node<E> succ)方法:

   /**
     * 将e插入succ结点前面
     *
     * @param e
     * @param succ
     */
    void linkBefore(E e, Node<E> succ) {
        // 前提:assert succ != null;

        // 获取succ结点的前继结点,赋值给pred
        final Node<E> pred = succ.prev;
        // 创建一个新结点(前继:pred,后继:succ)
        final Node<E> newNode = new Node<>(pred, e, succ);
        // 把newNode赋值为succ结点的前继结点
        succ.prev = newNode;
        // pred为空,证明该结点为头结点
        if (pred == null) {
            // 把newNode设置成头结点
            first = newNode;

        }else {// pred不为空
            // 把newNode赋值为pred的后继结点
            pred.next = newNode;
        }
        // 结点数量加一
        size++;
        // modCount自增一
        modCount++;
    }

 操作链表如下图: 

情况1:在A结点前,插入G结点:

 情况2:在B结点前,插入G结点:

(3)E unlinkFirst(Node<E> f)方法:

  /**
     * 移除第一个结点f(头结点)
     *
     * @param f
     * @return
     */
    private E unlinkFirst(Node<E> f) {
        // 前提: assert f == first && f != null;
        // 获取结点f中的元素
        final E element = f.item;
        // 获取结点f的后继结点
        final Node<E> next = f.next;
        // 把结点f中的元素设置为空
        f.item = null;
        // 把结点f后继结点设置为空,help GC(帮助GC)
        f.next = null;
        // 把next设置为头结点
        first = next;
        // next结点为空,则证明链表中只有一个结点,即只有头结点一个
        if (next == null) {
            // 把last设置为空
            last = null;
        }else { //next结点不为空
            // 把next的前继设置为空,即后继结点就变成头结点了
            next.prev = null;
        }
        // 结点数量减一
        size--;
        // modCount自增一
        modCount++;
        // 返回移除的元素
        return element;
    }

  操作链表如下图: 

Tips:unlinkLast(Node<E> l)方法与 unlinkFirst(Node<E> f)类似,我就不展示了

(4)E unlink(Node<E> x)方法:

   /**
     * 移除链表上的x结点
     *
     * @param x
     * @return
     */
    E unlink(Node<E> x) {
        // 前提: assert x != null;
        // 获取结点x中的元素
        final E element = x.item;
        // 获取结点x的后继结点
        final Node<E> next = x.next;
        // 获取结点x的前继结点
        final Node<E> prev = x.prev;
        // 前继结点为空,则证明该结点为头结点
        if (prev == null) {

            // 把后继结点设置为头结点
            first = next;
        } else { // 前继结点不为空

            // 把前继结点的后继设置为next
            prev.next = next;
            // 把x结点的前继设置为null
            x.prev = null;
        }
        // 后继结点为空,则证明该结点为尾结点
        if (next == null) {

            // 把前继结点设置为尾结点
            last = prev;
        } else { // 后继结点不为空

            // 把后继结点的前继设置为prev
            next.prev = prev;
            // 把x结点的后继设置为null
            x.next = null;
        }
        // 把结点x中的元素设置为空
        x.item = null;
        // 结点数量减一
        size--;
        // modCount自增一
        modCount++;
        // 返回移除的元素
        return element;
    }

 操作链表如下图: 

5.List接口相关方法:

(1) E get( int index)方法:

  /**
     * 返回此列表中指定位置的元素
     *
     * @param index index of the element to return
     * @return the element at the specified position in this list
     * @throws IndexOutOfBoundsException {@inheritDoc}
     */
    @Override
    public E get(int index) {
        // 查询索引的范围
        checkElementIndex(index);
        // 返回元素
        return node(index).item;
    }

步骤:1.查询索引范围;2.返回元素。 

checkElementIndex(int index)方法:

  /**
     * 检查元素索引
     * @param index
     */
    private void checkElementIndex(int index) {
        if (!isElementIndex(index)) {
            throw new IndexOutOfBoundsException(outOfBoundsMsg(index));
        }
    }

isElementIndex(int index)方法:

    /**
     * 判断元素的索引范围
     *
     * @param index
     * @return
     */
    private boolean isElementIndex(int index) {
        return index >= 0 && index < size;
    }

node(int index)方法:

   /**
     * 返回指定元素索引处的(非空)结点。
     *
     * @param index
     * @return
     */
    Node<E> node(int index) {
        // 前提:assert isElementIndex(index);
        // index值小于一半的size时,从头结点遍历
        if (index < (size >> 1)) {
            Node<E> x = first;
            for (int i = 0; i < index; i++) {
                x = x.next;
            }
            return x;
        } else {   // index值大于一半的size时,从尾结点遍历
            Node<E> x = last;
            for (int i = size - 1; i > index; i--) {
                x = x.prev;
            }
            return x;
        }
    }

Tips:这里遍历元素就进行一个优化。

(2) boolean add(E e)方法:


    /**
     * 将指定的元素追加到此列表的末尾
     *
     * 此方法相当于{@link #addLast}.
     *
     * @param e element to be appended to this list
     * @return {@code true} (as specified by {@link Collection#add})
     */
    @Override
    public boolean add(E e) {
        linkLast(e);
        return true;
    }

步骤:1.添加一个尾结点元素;2.返回 true。

(3)boolean remove()方法:

  /**
     * 从列表中删除指定元素的第一个匹配项(如果存在)。如果此列表不包含元素,则它将保持不变。
     * 更正式地说,删除索引{@code i}最低的元素。
     * 这样 o==null?get(i)==null:o.equals(get(i)),如果存在这样的元素,如果此列表返回true,
     * 包含指定的元素(如果此列表由于调用而更改)。
     *
     * @param o element to be removed from this list, if present
     * @return {@code true} if this list contained the specified element
     */
    @Override
    public boolean remove(Object o) {
        // 对象为空
        if (o == null) {
            // 循环遍历
            for (Node<E> x = first; x != null; x = x.next) {
                // 删除元素
                if (x.item == null) {
                    // 移除链表上的x结点
                    unlink(x);
                    return true;
                }
            }
        } else {
            // 循环遍历
            for (Node<E> x = first; x != null; x = x.next) {
                // 删除元素
                if (o.equals(x.item)) {
                    // 移除链表上的x结点
                    unlink(x);
                    return true;
                }
            }
        }
        // 不存在元素 false
        return false;
    }

 步骤:1.判断对象是否为空;2.为空遍历时,使用==判断是否相等,再删除;不为空遍历时,使用equals判断是否相等,再删除。

Tips:这里只是删除索引最低的元素。

(4)void clear()方法:

  /**
     * 从列表中删除所有元素
     * 此调用返回后,列表将为空
     *
     */
    @Override
    public void clear() {
        // 清除结点之间的所有链接是“不必要的”,但是:
        //-如果丢弃的结点驻留在多个生成中,则帮助生成GC  ??
        //-即使存在可访问的迭代器,也一定要释放内存 ??
        for (Node<E> x = first; x != null; ) {
            Node<E> next = x.next;
            x.item = null;
            x.next = null;
            x.prev = null;
            x = next;
        }
        first = last = null;
        size = 0;
        modCount++;
    }

  步骤:循环遍历把所有节点的前继结点、元素、后继结点都设置为空,然后被GC回收。

6.Queue接口相关方法:

(1) E peek()方法:

   /**
     * 检索但不删除此列表的头(第一个元素),
     * 有返回元素,无返回null
     *
     * @return 此列表的头,如果此列表为空,则为null
     * @since 1.5
     */
    @Override
    public E peek() {
        // 获取头结点
        final Node<E> f = first;
        // 获取头结点的元素
        return (f == null) ? null : f.item;
    }

步骤:1.获取头结点;2.获取并返回头结点的元素 

(2)E element() 方法:

  /**
     * 检索但不删除此列表的头(第一个元素)。
     * 有返回元素,无抛NoSuchElementException
     *
     * @return the head of this list
     * @throws NoSuchElementException if this list is empty
     * @since 1.5
     */
    @Override
    public E element() {
        // 返回此列表中的第一个元素
        return getFirst();
    }

(3) E poll() 方法:

   /**
     * 检索并删除此列表的头(第一个元素)
     * 有删除元素,无返回null
     *
     * @return the head of this list, or {@code null} if this list is empty
     * @since 1.5
     */
    @Override
    public E poll() {
        // 获取头结点
        final Node<E> f = first;
        // 删除头结点
        return (f == null) ? null : unlinkFirst(f);
    }

步骤:1.获取头结点;2.删除结点并返回删除结果

(4) E remove() 方法:

 /**
     * 检索并删除此列表的头(第一个元素)
     * 有删除元素,无抛NoSuchElementException
     *
     * @return the head of this list
     * @throws NoSuchElementException if this list is empty
     * @since 1.5
     */
    @Override
    public E remove() {
        // 从列表中删除并返回第一个元素
        return removeFirst();
    }

(5)boolean offer(E e) 方法:

  /**
     * 将指定元素添加为此列表的尾部(最后一个元素)
     *
     * @param e the element to add
     * @return {@code true} (as specified by {@link Queue#offer})
     * @since 1.5
     */
    @Override
    public boolean offer(E e) {
        return add(e);
    }

7.Deque接口相关方法:

(1)boolean offerFirst(E e)方法:


    /**
     * 在列表的前面插入指定的元素
     *
     * @param e the element to insert
     * @return {@code true} (as specified by {@link Deque#offerFirst})
     * @since 1.6
     */
    @Override
    public boolean offerFirst(E e) {
        // 在列表的开头插入指定的元素
        addFirst(e);
        // 返回true
        return true;
    }

步骤1.在列表的开头插入指定的元素;2.返回 true。

(2)boolean offerLast(E e)方法:

  /**
     * 在列表末尾插入指定元素
     *
     * @param e the element to insert
     * @return {@code true} (as specified by {@link Deque#offerLast})
     * @since 1.6
     */
    @Override
    public boolean offerLast(E e) {
        // 将指定的元素追加到此列表的末尾
        addLast(e);
        // 返回true
        return true;
    }

 步骤1.将指定的元素追加到此列表的末尾;2.返回 true。

(3) E peekFirst() 方法:

 /**
     * 检索但不删除此列表的第一个元素,如果此列表为空,则返回{@code null}。
     *
     * @return the first element of this list, or {@code null}
     *         if this list is empty
     * @since 1.6
     */
    @Override
    public E peekFirst() {
        // 获取头结点
        final Node<E> f = first;
        // 返回元素
        return (f == null) ? null : f.item;
    }

 步骤1.获取头结点;2.返回元素。 

(4)E peekLast() 方法:

  /**
     * 检索但不删除此列表的最后一个元素,如果此列表为空,则返回{@code null}。
     *
     * @return the last element of this list, or {@code null}
     *         if this list is empty
     * @since 1.6
     */
    @Override
    public E peekLast() {
        // 获取尾结点
        final Node<E> l = last;
        // 返回元素
        return (l == null) ? null : l.item;
    }

 步骤1.获取尾结点;2.返回元素。  

(5)E pollFirst() 方法:

 /**
     * 检索并删除此列表的第一个元素,如果此列表为空,则返回{@code null}。
     *
     * @return the first element of this list, or {@code null} if
     *     this list is empty
     * @since 1.6
     */
    @Override
    public E pollFirst() {
        // 获取头结点
        final Node<E> f = first;
        // 删除元素
        return (f == null) ? null : unlinkFirst(f);
    }

 步骤1.获取头结点;2.删除元素并返回结果。   

(6) E pollLast() 方法:

 /**
     * 检索并删除此列表的最后一个元素,如果此列表为空,则返回null
     *
     * @return the last element of this list, or {@code null} if
     *     this list is empty
     * @since 1.6
     */
    @Override
    public E pollLast() {
        // 获取尾结点
        final Node<E> l = last;
        // 删除元素
        return (l == null) ? null : unlinkLast(l);
    }

 步骤1.获取尾结点;2.删除元素并返回结果。   

(7)void push(E e)方法:

  /**
     * 将元素推送到由该列表,表示的堆栈上。换句话说,在列表的前面插入元素。
     *
     * <p>This method is equivalent to {@link #addFirst}.
     *
     * @param e the element to push
     * @since 1.6
     */
    @Override
    public void push(E e) {
        // 在列表的开头插入指定的元素
        addFirst(e);
    }

(8)E pop()方法:

  /**
     * 从该列表表示的堆栈中弹出一个元素。换句话说,删除并返回此列表的第一个元素。
     *
     * 这种方法相当于 {@link #removeFirst()}.
     *
     * @return the element at the front of this list (which is the top
     *         of the stack represented by this list)
     * @throws NoSuchElementException if this list is empty
     * @since 1.6
     */
    @Override
    public E pop() {
        // 从列表中删除并返回第一个元素
        return removeFirst();
    }

三、MyArrayList类源码:

springboot_demo: springboot_demo 《 springboot-java-basic-note模块-sourcecode.list.linkedlist包》


四、参考文件:

 JDK1.8 LinkedList类源码

  • 4
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 2
    评论
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

hanxiaozhang2018

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值