安卓LinkedList源码解析

前话:本次解析基于JDK1.8.0,SDK26。

一、数据结构

和ArrayList基于数组的不同,LinkedList实现思想是基于双向链表。所以在LinkedList类的内部,维护了链表的表头和表尾两个节点。有了表头和表尾节点,当然就可以对链表做任何想要的操作了。
既然是是基于链表,那么肯定离不开链表中每一个节点的定义,LinkedList内部维护了一个静态内部类Node.class(静态内部类有很多优势,自行脑补吧)用来表示节点。

//泛型,表示我们可以存储任意类型(基本类型除外)
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;
        }
    }

二、构造方法

在看构造方法之前,先看一下LinkedList类的重要的成员变量:

//当前链表的长度,(transient为序列化知识点,先不讨论)
transient int size = 0;
//表头,默认为null
transient Node<E> first;
//表尾,默认为null
transient Node<E> last;
1、最简单的无参构造方法
    //没错,就是什么都没有干!LinkedList只有在真正添加元素的时候才会初始化新节点,并把新节点连在链表上。
    public LinkedList() {
    }
2、有参构造方法:
public LinkedList(Collection<? extends E> c) {
        //调用无参构造方法,还是什么都没干。。。
        this();
        //划重点,将传入的集合参数全部添加到链表,后续分析addAll。
        addAll(c);
    }

三、add方法

public boolean add(E e) {
        //直接调用linkLast,把元素e直接link到链表的尾部。
        linkLast(e);
        return true;
    }

void linkLast(E e) {
        //将尾结点暂时赋值给 l
        final Node<E> l = last;
        //真正创建节点的地方!!!
        final Node<E> newNode = new Node<>(l, e, null);
        //将新节点重新置为尾节点
        last = newNode;
        if (l == null)
            // l==null,说明是第一次添加数据,直接链表中只有一个节点,既是头结点也是尾节点
            first = newNode;
        else
            //将新的节点连接到原来的尾节点
            l.next = newNode;
        //每增加一个元素,size++
        size++;
        modCount++;
    }

当然,默认情况下add方法会将新的数组直接连接到链表的尾部,LinkedList也提供了其他的add方法:addFirst(),addLast(),在添加元素的时候link到链表的不同位置。但是原理都是对链表的link操作。

四、get方法

根据元素的下标获取元素,先检查下标的合法性,如果合法再去链表中循环找到对应的下标。

public E get(int index) {
        //检查合法性
        checkElementIndex(index);
        //node(index)方法会返回index对应的Node节点,然后返回节点的item。
        return node(index).item;
    }

private void checkElementIndex(int index) {
        if (!isElementIndex(index))
            throw new IndexOutOfBoundsException(outOfBoundsMsg(index));
    }

Node<E> node(int index) {
        //size>>1,位运算,相当于size/2
        //二分法的思想
        if (index < (size >> 1)) {
            //如果index小于链表长度的一半,那么从链表头循环查找index对应的node
            Node<E> x = first;
            for (int i = 0; i < index; i++)
                x = x.next;
            return x;
        } else {
            //如果index大于链表长度的一半,则从链表尾循环递减查找
            Node<E> x = last;
            for (int i = size - 1; i > index; i--)
                x = x.prev;
            return x;
        }
    }

可以看出,在使用index查找元素的时候,LinkedList的性能要远低于ArrayList。

五、set方法

 public E set(int index, E element) {
        //检查index合法性
        checkElementIndex(index);
        //查找链表中的index位置的节点并赋值给x
        Node<E> x = node(index);
        //直接将新的value替换
        E oldVal = x.item;
        x.item = element;
        //返回就得vaule
        return oldVal;
    }

六、add(index,e)方法

向指定的位置index添加元素

public void add(int index, E element) {
        //检查合法性
        checkPositionIndex(index);
        //如果index == size,就直接将该元素link到链表的尾部
        if (index == size)
            linkLast(element);
        else
            //1、node(index)方法先找到index对应的原来的元素,然后再把新的元素link进去
            //例如:假设链表有2,5,3三个元素,现在要把4插入到index==1的位置,也就是link到5的前边。
            linkBefore(element, node(index));
    }

七、remove方法

public E remove(int index) {
        //检查合法性
        checkElementIndex(index);
        //调用unlink方法,也就是把这个节点从当前的链表中解构出来。
        return unlink(node(index));
    }

八、LinkedList和ArrayList的性能比较:

1、ArrayList:

在数组尾部添加和删除数据,性能开销很小,只是有时候需要对数组扩容。在数组中部添加和删除数据,需要将该数据后边的所有数据平移,性能开销很大。在进行按index查找数据时,数组的结构发挥了很大的优势,性能最高。在按value查找的时候,实际是将整个数组遍历一遍,性能较差。

2、LinkedList:

在链表尾部或头部添加和删除数据,性能开销很小。在链表的中部添加删除数据时,由于链表的特性,只需要操作该元素的前后指针,性能最高。在按index进行查找,LinkedList做了一次二分查找,但是还是需要循环链表到index的位置,所以性能很差。按value查找的时候,也是遍历整个链表,性能也很差。

总结:

当需要频繁的删除和插入数据时,使用LinkedList。
当需要频繁的按Index不断查找数据时,使用ArrayList。

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

推荐阅读更多精彩内容