Java数据结构详解(四)-LinkedList

来源:互联网 发布:人工智能bob和alice 编辑:程序博客网 时间:2024/06/05 23:46

LinkedList源码详解

一, LinkedList字段

    //返回链表的逻辑大小    transient int size = 0;     //头部节点    transient Node<E> first;     //尾部节点    transient Node<E> last;

二,LinkedList构造方法

一,无参构造器

public LinkedList() {}

二,有参数的构造器

    public LinkedList(Collection<? extends E> c) {        this();//调用无参构造器        addAll(c);//调用 addAll()    }

addAll(Collection

    public boolean addAll(Collection<? extends E> c) {        return addAll(size, c);//调用addAll(size ,c)    }

addAll(int index, Collection

    public boolean addAll(int index, Collection<? extends E> c) {        //检查index是不是超出范围了,size > index >= 0        checkPositionIndex(index);        //将c转换为数组。        Object[] a = c.toArray();        //得当新增的数组的长度        int numNew = a.length;        //检查是不是空数组        if (numNew == 0)            return false;        定义两个节点(没有new) 上一个节点:pred ,下一个节点:succ        Node<E> pred, succ;        //如果index == size 那么说明是在双向链表尾部插入。        if (index == size) {            //将节点succ指向null;            succ = null;            //将节点pred指向原链表中的最后一个节点            pred = last;        } else {            //如果index !=size 那么插入位置应该在头部或者中间部位            //将节点succ指向index位置的节点            succ = node(index);            //将节点pred 指向index位置的节点的上一个节点。            pred = succ.prev;        }        //foreach循环        for (Object o : a) {            @SuppressWarnings("unchecked") E e = (E) o;            //new 一个新节点,节点的上一个节点是pred,数据是e,下一个节点是null;            Node<E> newNode = new Node<>(pred, e, null);            //如果上一个节点为null,说明是在头部插入的数据,只需要将插入数据的第一个数据设置为头部节点。            if (pred == null)                first = newNode;            else                //上一个节点不为空,则把上一个节点的下一个节点的指向新建节点。这样 双向链表的指向就完成了。                pred.next = newNode;            //将节点重新指向pred            pred = newNode;        }        //在链表尾部插入数据,succ == null         if (succ == null) {            //将循环完成之后的最后一个pred指向last;            last = pred;        } else {            //将succ指向循环完成之后的最后一个pred的下一个节点            pred.next = succ;            //将指向循环完成之后的最后一个pred指向succ 的上一个节点,双向链表 完成。            succ.prev = pred;        }        //size = size + numNew ;        size += numNew;        modCount++;        return true;    }

Node node(int index)

    Node<E> node(int index) {        // assert isElementIndex(index);        //如果index<siez/2        if (index < (size >> 1)) {            Node<E> x = first;            for (int i = 0; i < index; i++)                x = x.next;            return x;        } else {            Node<E> x = last;            for (int i = size - 1; i > index; i--)                x = x.prev;            return x;        }    }

构造方法结束

LinkedList的增删改查


向链表中插入数据 分为三种情况 :

一,在链表头部插入数据:
public void addFirst(E e)

    public void addFirst(E e) {        linkFirst(e);    }    private void linkFirst(E e) {        //将第一个节点first赋值给f。        final Node<E> f = first;        //new一个新的 Node节点,新节点的上一个节点指向null,数据为e,下一个节点为f。        final Node<E> newNode = new Node<>(null, e, f);        //更新first节点        first = newNode;        如果f==null 说明是一个空链表。则first和last都指向新建的节点。        if (f == null)            last = newNode;        else            //将f的上一个节点指向新建节点。            f.prev = newNode;        size++;        modCount++;    }

二,在链表的尾部插入数据:

public void addLast(E e)

    public boolean add(E e) {        linkLast(e);        return true;    }    public void addLast(E e) {        linkLast(e);    }    void linkLast(E e) {        final Node<E> l = last;        final Node<E> newNode = new Node<>(l, e, null);        last = newNode;        if (l == null)            first = newNode;        else            l.next = newNode;        size++;        modCount++;    }

三,在链表的中部插入数据:
public void add(int index, E element)

    public void add(int index, E element) {        //检查index有没有超过范围        checkPositionIndex(index);        //如果index == size ,就相当于在链表尾部增加数据,则调用linkLast();        if (index == size)            linkLast(element);        else            //否则调用linkBefore()            linkBefore(element, node(index));    }    void linkBefore(E e, Node<E> succ) {        // assert succ != null;        //拿到succ的上一个节点(succ 为index指向的节点)        final Node<E> pred = succ.prev;        //新建一个节点,其上一个节点为 succ的上一个节点,数据为 e,下一个节点为 succ。这样一个单向链表就完成了。        final Node<E> newNode = new Node<>(pred, e, succ);        //succ的上一个节点指向新建节点。        succ.prev = newNode;        //pred 为null则表示新增节点为头部节点。        if (pred == null)            first = newNode;        else            //succ的上一个节点的下一个节点的引用指向新建节点。            pred.next = newNode;        size++;        modCount++;    }

public E remove(int index) {}

    public E remove(int index) {        //检查index是不是超出范围了        checkElementIndex(index);        //返回调用unlink方法,参数为 node(index)        return unlink(node(index));    }

E unlink(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;        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;    }

public E set(int index, E element) {}

    public E set(int index, E element) {        //检查index        checkElementIndex(index);        //找到index的节点        Node<E> x = node(index);        E oldVal = x.item;        修改数据        x.item = element;        返回数据修改之前的值        return oldVal;    }

public E get(int index) {}

    public E get(int index) {        checkElementIndex(index);        return node(index).item;    }

链表的 get 和 set 都调用了方法 node(index)

而node方法内部是 循环遍历数组. 所以get和set方法比较耗时.

而remove和add 方法只是修改了 对象的引用.时间短效率高.

ArrayList 和 LinkedList 相比

ArrayList查询和修改 的复杂度为O(1); 增加和删除的时间复杂度为O(N)

LinkedList查询和修改的时间复杂度为O(N/2),增加和删除的时间复杂度为O(1);

原创粉丝点击