安卓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。

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。

推荐阅读更多精彩内容