Java提高篇(二)----LinkedList

一、LinkedList简介

LinkedList继承了AbstractSequentialList类,实现了List,Deque,Cloneable,Serializable接口。

二、LinkedList源码分析

LinkedList底层是一个双向链表,支持大量的随机新增,删除操作,较ArrayList不支持大量随机的数据访问。

2.1底层实现--双向链表

/**
 * 内部类,链的节点
 * item节点元素
 * next后继
 * prev前驱
 * */
private static class Node<E> {
    E item;
    Node<E> next;
    Node<E> prev;

    /**
     * prev前驱
     * element当前元素
     * next后继
     * */
    Node(Node<E> prev, E element, Node<E> next) {
        this.item = element;
        this.next = next;
        this.prev = prev;
    }
}

2.2成员变量

/**
  * 链的大小/长度
  * */
 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.3构造方法

LinkedList提供了两个构造方法:
1.public LinkedList();构造一个空的list
2.public LinkedList(Collection<? extends E> c) ;构造一个包含指定元素集的list;

/**
  * 构造一个空的list
  * */
 public LinkedList() {
    }
 
 /**
  * 构造一个包含指定元素集的list,该元素集按照迭代器的输出系列进行排列
  * */
 public LinkedList(Collection<? extends E> c) {
     //先构造一个空的list
        this();
        //再进行添加
        addAll(c);
    }

2.4新增方法

LinkedList提供了如下几个新增方法:
1.public void addFirst(E e);在列表头部插入指定元素e
2.public void addLast(E e);在列表尾部插入指定元素e
3.public boolean add(E e) ;在列表尾部插入指定元素e,返回boolean类型值true;该方法和#addLast方法类似
4.public boolean addAll(Collection<? extends E> c);追加元素集c到列表尾部
5.public boolean addAll(int index, Collection<? extends E> c);添加元素集c到指定位置
6.public void add(int index, E element);添加元素e到指定位置
源码解析:

   /**
     * 在链表头部插入一个元素
     *
     * @param e 待插入元素
     */
    public void addFirst(E e) {
        //指向头部操作
        linkFirst(e);
    }

    /**
     * 在链表尾部追加一个元素e
     *
     * <p>This method is equivalent to {@link #add}.
     * equivalent等价的
     * @param e 待插入元素
     */
    public void addLast(E e) {
        linkLast(e);
    }
    /**
     * 追加指定元素到链表尾部,该方法与addLast方法是相等的
     *
     * <p>This method is equivalent to {@link #addLast}.
     *
     * @param e element to be appended to this list
     * @return {@code true} (as specified by {@link Collection#add})
     */
    public boolean add(E e) {
        linkLast(e);
        return true;
    }
    
    
    /**
     * 
     * 追加元素集到链表尾,
     * Appends all of the elements in the specified collection to the end of
     * this list, in the order that they are returned by the specified
     * collection's iterator.  The behavior of this operation is undefined if
     * the specified collection is modified while the operation is in
     * progress.  (Note that this will occur if the specified collection is
     * this list, and it's nonempty.)
     *
     * @param c collection containing elements to be added to this list
     * @return {@code true} if this list changed as a result of the call
     * @throws NullPointerException if the specified collection is null
     */
    public boolean addAll(Collection<? extends E> c) {
        return addAll(size, c);
    }

    /**
     * 
     * 添加指定元素集到指定位置
     * Inserts all of the elements in the specified collection into this
     * list, starting at the specified position.  Shifts the element
     * currently at that position (if any) and any subsequent elements to
     * the right (increases their indices).  The new elements will appear
     * in the list in the order that they are returned by the
     * specified collection's iterator.
     *
     * @param index index at which to insert the first element
     *              from the specified collection
     * @param c collection containing elements to be added to this list
     * @return {@code true} if this list changed as a result of the call
     * @throws IndexOutOfBoundsException {@inheritDoc}
     * @throws NullPointerException if the specified collection is null
     */
    public boolean addAll(int index, Collection<? extends E> c) {
        //检查位置
        checkPositionIndex(index);
        //元素集转成对象数组
        Object[] a = c.toArray();
        //数组长度
        int numNew = a.length;
        if (numNew == 0)
            return false;

        //前驱和后继节点
        Node<E> pred, succ;
        //如果当前下标值为链的长度,则后继为空,前驱节点指向最后一个节点
        //否则找到指定下标的元素,并且该元素为后继节点,前驱节点是该元素的前驱节点
        if (index == size) {
            succ = null;
            pred = last;
        } else {
            succ = node(index);
            pred = succ.prev;
        }
        /**
         * 遍历数组
         * */
        for (Object o : a) {
            /**
             * 声明未检查
             * */
            @SuppressWarnings("unchecked") E e = (E) o;
            /**
             * 创建新的链
             * */
            Node<E> newNode = new Node<>(pred, e, null);
            if (pred == null)
                first = newNode;
            else
                pred.next = newNode;
            pred = newNode;
        }

        if (succ == null) {
            last = pred;
        } else {
            pred.next = succ;
            succ.prev = pred;
        }

        size += numNew;
        modCount++;
        return true;
    }
    
    /**
     * 插入指定元素到指定位置
     * 
     * Inserts the specified element at the specified position in this list.
     * Shifts the element currently at that position (if any) and any
     * subsequent elements to the right (adds one to their indices).
     *
     * @param index index at which the specified element is to be inserted
     * @param element element to be inserted
     * @throws IndexOutOfBoundsException {@inheritDoc}
     */
    public void add(int index, E element) {
        /**位置检查*/
        checkPositionIndex(index);
        
        //如果下标等于链长,则将元素插入链尾
        if (index == size)
            linkLast(element);
        else
            linkBefore(element, node(index));
    }

其中比较重要的操作链表的方法:
1.private void linkFirst(E e);指向第一个元素之前(即元素e作为第一个节点)
2.void linkLast(E e) ;在最后一个元素之后(即元素e作为尾节点)
3.void linkBefore(E e, Node<E> succ);在非空元素succ前插入元素e
源码分析:

/**
 * 插入第一个元素之前
 * */
private void linkFirst(E e) {
    // 得到第一个元素
    final Node<E> f = first;
    // 新的节点,前驱为空,后继为当前节点的第一个元素
    final Node<E> newNode = new Node<>(null, e, f);
    // 新的第一个元素
    first = newNode;
    // 如果之前第一个元素为空,则最后一个元素为新的节点元素,否则之前元素的前驱为当前元素
    if (f == null)
        last = newNode;
    else
        f.prev = newNode;
    size++;
    modCount++;
}

/**
 * 在最后一个元素之后插入一个元素
 * */
void linkLast(E e) {
    // 最后一个元素
    final Node<E> l = last;
    // 当前节点,该元素的前驱是l,该元素的后继是null
    final Node<E> newNode = new Node<>(l, e, null);
    // 新的最后一个元素是当前新的元素
    last = newNode;
    // 如果之前最后一个元素为空,则第一个元素为当前元素,否则,原来最后一个元素的后继指向该元素
    if (l == null)
        first = newNode;
    else
        l.next = newNode;
    size++;
    modCount++;
}

/**
 * 在一个非空元素succ前插入一个元素e
 * 
 * */
void linkBefore(E e, Node<E> succ) {

    // succ不为空,断言机制
    // assert succ != null;
    // 元素succ的前驱节点
    final Node<E> pred = succ.prev;
    final Node<E> newNode = new Node<>(pred, e, succ);
    // 元素succ的前驱指向newNode
    succ.prev = newNode;
    // 如果前驱节点为空,则newNode作为第一个节点
    // 否则前驱节点的后继指向newNode
    if (pred == null)
        first = newNode;
    else
        pred.next = newNode;
    size++;
    modCount++;
}

2.5移除方法

LinkedList提供了如下几个移除方法:
1.public E remove(int index);移除index处元素,并返回该元素
2.public E remove() ;移除头部元素,并返回该被移除的元素,如果列表为空,则抛出异常
3.public E removeFirst();移除头部元素,并返回该被移除的元素,如果列表为空,则抛出异常
源码分析:

/**
 * 移除index处的元素,并返回该元素
 * @param index the index of the element to be removed
 * @return the element previously at the specified position
 * @throws IndexOutOfBoundsException {@inheritDoc}
 */
public E remove(int index) {
    /*下标检查*/
    checkElementIndex(index);
    return unlink(node(index));
}

/**
 * 移除头部元素,返回列表头部元素,如果列表为空,则抛出异常
 *
 * @return the head of this list
 * @throws NoSuchElementException if this list is empty
 * @since 1.5
 */
public E remove() {
    //返回列表第一个元素
    return removeFirst();
}


/**
 * 移除列表第一个元素,并返回该元素,如果列表为空,则抛出异常
 *
 * @return the first element from this list
 * @throws NoSuchElementException if this list is empty
 */
public E removeFirst() {
    //指向列表头部元素
    final Node<E> f = first;
    if (f == null)
        throw new NoSuchElementException();
    //解链
    return unlinkFirst(f);
}

其中操作链表的方法:
1.E unlink(Node<E> x);将非空元素X解链
源码分析:

/**
 * 解链
 * Unlinks non-null node x.
 */
E unlink(Node<E> x) {
    // assert x != null;
    final E element = x.item;
    final Node<E> next = x.next;
    final Node<E> prev = x.prev;

    //如果前驱为空,则首元素为x后继
    //否则前驱的后继指向x的后继,x的前驱指向null
    if (prev == null) {
        first = next;
    } else {
        prev.next = next;
        x.prev = null;
    }

    //如果后继为空,则尾元素为x的前驱
    //否则,后继的前驱指向x的前驱,x的后继指向空
    if (next == null) {
        last = prev;
    } else {
        next.prev = prev;
        x.next = null;
    }

    //链表长度减一
    x.item = null;
    size--;
    modCount++;
    return element;
}
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 196,165评论 5 462
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 82,503评论 2 373
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 143,295评论 0 325
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 52,589评论 1 267
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 61,439评论 5 358
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 46,342评论 1 273
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 36,749评论 3 387
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 35,397评论 0 255
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 39,700评论 1 295
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 34,740评论 2 313
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 36,523评论 1 326
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 32,364评论 3 314
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 37,755评论 3 300
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,024评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 30,297评论 1 251
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 41,721评论 2 342
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 40,918评论 2 336

推荐阅读更多精彩内容

  • 一、基本数据类型 注释 单行注释:// 区域注释:/* */ 文档注释:/** */ 数值 对于byte类型而言...
    龙猫小爷阅读 4,246评论 0 16
  • 1. Java基础部分 基础部分的顺序:基本语法,类相关的语法,内部类的语法,继承相关的语法,异常的语法,线程的语...
    子非鱼_t_阅读 31,514评论 18 399
  • java笔记第一天 == 和 equals ==比较的比较的是两个变量的值是否相等,对于引用型变量表示的是两个变量...
    jmychou阅读 1,477评论 0 3
  • 爱情从来都是属于自己的,无论是被爱还是你爱。当爱深入骨髓时,所有的付出都是值得的。 坚持原创,转载或约稿请简信本人...
    珠海红叶原创阅读 181评论 1 2
  • 早上出门,从楼下骑摩拜去往地铁站。路过一段林荫路,散落着些树叶在地上。一位60来岁身形较瘦的大爷在打扫,橘黄色马甲...
    土豆南瓜阅读 152评论 0 2