LinkedList简介


LinkedList概述

LinkedList底层通过双向集合的数据结构实现,LinkedList可以作为List使用,也可以作为队列和栈使用。支持从集合的头部,中间,尾部进行添加、删除等操作。LinkedList的继承与实现的关系图如下所示。

LinkedL ist继承与实现的关系图.jpg

  • LinkedList同时实现了List接口和Deque接口,也就是说它既可以看作一个顺序容器,又可以看作一个队列(Queue),同时又可以看作一个栈(Stack)。这样看来,LinkedList简直就是个全能冠军。当你需要使用栈或者队列时,可以考虑使用LinkedList,一方面是因为Java官方已经声明不建议使用Stack类,更遗憾的是,Java里根本没有一个叫做Queue的类(它是个接口名字)。关于栈或队列,现在的首选是ArrayDeque,它有着比LinkedList(当作栈或队列使用时)有着更好的性能。

  • 双向链表的每个节点用内部类Node表示。LinkedList通过firstlast引用分别指向链表的第一个和最后一个元素。当链表为空的时候first和last都指向null。

双链表结构示意图.png
  // 一个私有的内部类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;
        }
    }

预热Deque接口

  • ArrayDequeLinkedListDeque的两个通用实现,由于官方更推荐使用AarryDeque用作栈和队列

  • 从名字可以看出ArrayDeque底层通过数组实现,为了满足可以同时在数组两端插入或删除元素的需求,该数组还必须是循环的,即循环数组(circular array),也就是说数组的任何一点都可能被看作起点或者终点。ArrayDeque是非线程安全的(not thread-safe),当多个线程同时使用的时候,需要手动同步;另外,该容器不允许放入null元素。

看看源码怎么说

添加Add

//调用add默认会添加到链表末尾
public boolean add(E e) {
        linkLast(e);
        return true;
    }
void linkLast(E e) {
        //得到最后一个节点的指针,
        final Node<E> l = last;
        //封装一个节点 l为前一个节点引用地址 e真正存储的数据,后面存放后一个节点的引用
        final Node<E> newNode = new Node<>(l, e, null);
        //最后一个节点指向新增的节点地址
        last = newNode;
        if (l == null)
            //空链表中新添加的节点就是第一个
            first = newNode;
        else
            //非空链表中新添加的节点就是链表最后节点的下一个节点
            l.next = newNode;
        //链表长度
        size++;
        //扩容次数
        modCount++;
    }

void add(int index, E element)

add(int index, E element)的逻辑稍显复杂,可以分成两部,

1.先根据index找到要插入的位置;

2.修改引用,完成插入操作。

 public void add(int index, E element) {
        //位置索引检测 return index >= 0 && index <= size;
        checkPositionIndex(index);
        if (index == size)
            //末尾插入 详情见上面
            linkLast(element);
        else
            //根据位置插入数据 node(index)得到index位置的节点
            linkBefore(element, node(index));
    }
//处理index位置的节点succ
void linkBefore(E e, Node<E> succ) {
        //得到succ的前驱指针
        final Node<E> pred = succ.prev;
        //创建newNode节点,将newNode的后继指针指向succ,前驱指针指向pred
        final Node<E> newNode = new Node<>(pred, e, succ);
        //将succ的前驱指针指向newNode
        succ.prev = newNode;
        if (pred == null)
            //前驱指针为空 就是第一个节点
            first = newNode;
        else
            //前驱指针不为空 那么直接将pred的后继指针指向newNode即可
            pred.next = newNode;
        size++;
        modCount++;
    }
双链表添加元素示意图.png
  • 双链表 每个节点都有一个head前驱指针和tail后驱指针 前一个节点的tail指针指向后一个节点的head指针,如果后一个节点后面没有数据时,后一个节点的tail指针指向null ,但是双链表为空时headtail都指向null

删除

  • 移除一个对象从双向链表中 步骤

    • 得到三个节点 1.需要删除的节点(x) 前一个节点(prev) 后一个节点next

    • 把前一个节点的后驱指针tail指向后一个节点next

    • 再把后一个节点的前驱指针head指向前一个节点prev

    • 再把当前的节点x置空 就完成了双向链表的删除操作

public boolean remove(Object o) {
        //空对象的处理
        if (o == null) {
            //遍历节点
            for (Node<E> x = first; x != null; x = x.next) {\
                //节点数据为null
                if (x.item == null) {
                    unlink(x);
                    return true;
                }
            }
        } else {
            for (Node<E> x = first; x != null; x = x.next) {
                //节点数据和传递过来的数据一致
                if (o.equals(x.item)) {
                    unlink(x);
                    return true;
                }
            }
        }
        return false;
    }
 E unlink(Node<E> x) {
        //节点数据
        final E element = x.item;
        //节点的后驱指针指向的节点 后节点
        final Node<E> next = x.next;
        //节点的前驱指针指向的节点 前一个节点
        final Node<E> prev = x.prev;
        //前节点为空 表示删除第一个元素
        if (prev == null) {
            first = next;
        } else {
            //不为空就把前节点的后驱指针指向后节点
            prev.next = next;
            //当前节点的前驱指针指为空
            x.prev = null;
        }
        //后节点为空
        if (next == null) {
            //最后一个节点等于前节点
            last = prev;
        } else {
            //后节点的前驱指针指向前节点
            next.prev = prev;
            //当前节点的前驱指针指为空
            x.next = null;
        }
        //当前节点的前驱指针指为空
        x.item = null;
        size--;
        modCount++;
        return element;
    }

总结

  • 通过对LinkedList的源码解读,又会增加对数据结构双向链表的认识,

和ArrayList的对比

其实就是链表和数组的对比。插入,删除:

  • ArrayList:基于数组实现,通常情况下添加元素很简单,直接将元素添加到预先创建的数组中,没有额外操作。但当数组空间不足时,就涉及到扩容,数组的复制,性能很差。删除时,需要进行数组元素的复制,性能不高。

  • LinkedList:基于链表实现,可变长,单纯的插入和删除操作,都比较简单,修改下关联节点的前后"指针"即可。

查询

  • ArrayList:支持随机访问,查询效率高。时间复杂度O(1)。

  • LinkedList:不支持随机访问,按下标索引查询效率较低,尽量不要使用下标索引的方式进行元素的遍历,推荐使用迭代器

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

推荐阅读更多精彩内容

  • ArrayDeque Deque接口的大小可变数组的实现。 特性: 底层实现时循环数组 没有容量限制,在数组元素装...
    navyd阅读 1,588评论 0 2
  • LinkedList简介 LinkedList基于双向链表实现 LinkedList相对于Arraylist来说,...
    加大装益达阅读 1,017评论 0 0
  • LinkedList介绍 LinkedList底层所采用的数据结构是链表。链表是由多个节点构成,每个节点都包含三个...
    奇点一氪阅读 1,556评论 0 11
  • Java集合类可用于存储数量不等的对象,并可以实现常用的数据结构如栈,队列等,Java集合还可以用于保存具有映射关...
    小徐andorid阅读 1,918评论 0 13
  • 超级玛丽
    唯有源头活水来阅读 148评论 0 0