Java集合框架之LinkedList(三)

基于jdk(1.8.0_91)版本

一、LinkedList简介

  1. LinkedList是双链表结构,实现了List和Qeque的接口。实现所有可选的List操作,并允许所有元素,包括null。
  2. LinkedList是非线程安全的。如果在多线程环境下同时访问一个链表,当有一个线程修改了LinkedList,必须手动同步。可以在初始化当中通过List list = Collections.synchronizedList(new LinkedList(...))返回一个线程安全的集合。
  3. LinkedList实现了Serializable接口,支持序列化,能够通过数据传递。
  4. LinkedList实现了Cloneable接口,能被克隆。

二、LinkedList的继承关系

java.lang.Object 
    java.util.AbstractCollection<E> 
        java.util.AbstractList<E> 
            java.util.AbstractSequentialList<E> 
                java.util.LinkedList<E> 
public class LinkedList<E>
    extends AbstractSequentialList<E>
    implements List<E>, Deque<E>, Cloneable, java.io.Serializable

LinkedList与Collection的关系图:


从上面的UML图可以看出:有两个重要的参数分别是headersize

  1. henader:是双向链表的表头,表头的节点对象是个Node类实例(在jdk1.7之前是Entry)。Node类下包含了pervious、next、element这个成员变量,pervious表示上节点,next表示下节点,element表示集合中的元素值。
  2. size:双向链表的节点个数。

三、源码分析

public class LinkedList<E> extends AbstractSequentialList<E> implements
        List<E>, Deque<E>, Cloneable, java.io.Serializable {
    // 集合元素的个数
    transient int size = 0;
    // 指向第一个节点的指针
    transient Node<E> first;
    // 指向最后一个节点的指针
    transient Node<E> last;

    // 空实现的构造函数,默认函数
    public LinkedList() {
    }

    // 构造一个指定元素c的集合列表,并调用默认构造函数
    public LinkedList(Collection<? extends E> c) {
        this();
        addAll(c);
    }

    // 元素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++;
    }

    // 元素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++;
    }

    // 删除链接上非空的第一个元素f,私有函数,在removeFirst中调用
    private E unlinkFirst(Node<E> f) {
        // assert f == first && f != null;
        final E element = f.item;
        final Node<E> next = f.next;
        f.item = null;
        f.next = null; // help GC
        first = next;
        if (next == null)
            last = null;
        else
            next.prev = null;
        size--;
        modCount++;
        return element;
    }

    // 删除链接上最后一个非空元素l,在removeLast中调用
    private E unlinkLast(Node<E> l) {
        // assert l == last && l != null;
        final E element = l.item;
        final Node<E> prev = l.prev;
        l.item = null;
        l.prev = null; // help GC
        last = prev;
        if (prev == null)
            first = null;
        else
            prev.next = null;
        size--;
        modCount++;
        return element;
    }

    // 取消x节点上非空元素 很重要,对理解LinkedList是双链表结构有很大的帮助
    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节点是第一个节点
        if (prev == null) {
            first = next;
        } else {
            // 若x节点的上一节点非空时,会把x节点的next赋值给x节点的prev节点的下一节点next,
            //同时把x节点的指向切断赋null。
            prev.next = next;
            x.prev = null;
        }
        // 和上面情况一样的原理,这里是x节点下一节点的指向关系
        if (next == null) {
            last = prev;
        } else {
            next.prev = prev;
            x.next = null;
        }

        x.item = null;
        size--;
        modCount++;
        return element;
    }

    // 返回集合列表的第一个元素,列表为空会抛出NoSuchElementException
    public E getFirst() {
        final Node<E> f = first;
        if (f == null)
            throw new NoSuchElementException();
        return f.item;
    }

    // 返回集合列表的最后一个元素,列表为空会抛出NoSuchElementExceptio
    public E getLast() {
        final Node<E> l = last;
        if (l == null)
            throw new NoSuchElementException();
        return l.item;
    }

    // 从列表中删除并返回第一个元素,
    public E removeFirst() {
        final Node<E> f = first;
        if (f== null)
            throw new NoSuchElementException();
        return unlinkFirst(f);
    }

    // 从列表中删除并返回最后一个元素
    public E removeLast() {
        final Node<E> l = last;
        if(l == null)
            throw new NoSuchElementException();
        return unlinkLast(l);

    } 
    
    // 在列表的最头部插入指定元素e
    public void addFirst(E e) {
        linkFirst(e);
    }

    // 在列表的尾部插入指定元素e
    public void addLast(E e) {
        linkLast(e);
    }

    // 判断列表中是否包含元素o
    public boolean contains(Object o) {
        return indexOf(o)

        != -1;
    }

    // 列表元素实际个数
    public int size() {
        return size;
    }

    // 将元素e加入列表的末尾,其实是通过linkLast来实现的,返回true说明添加成功
    boolean add(E e) {

        linkLast(e);
        return true;
    }

    // 从列表中删除指定的元素o,通过遍历从第一个节点开始轮询查询是否存在o元素,若存在则删除o元素返回true。
    // 从remove方法内查询分了两种情况,o是否为空。
    public boolean remove(Object o) {
        if (o == null) {
            for (Node<E> x = first; x != null; x = x.next) {
                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;
    }

    // 在列表末尾添加集合c
    public boolean addAll(Collection<? extends E> c) {
        return addAll(size, c);
    }

    // 从双向链表的index开始,将“集合(c)”添加到双向链表中。
    public boolean addAll(int index, Collection<? extends E> c) {
        checkPositionIndex(index);
        //将集合c转成数组
        Object[] a = c.toArray();
        //获取集合c的大小
        int numNew = a.length;
        if (numNew == 0)
            return false;

        Node<E> pred, succ;
        //设置插入位置后的前后节点
        if (index == size) {
            //插入到末尾端,c的上一节点为原列表的last节点
            //c的下一节点为null
            succ = null;
            pred = last;
        } else {
            succ = node(index);
            pred = succ.prev;
        }
        //将集合c通过遍历全部插入双向链表中
        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;
        }
        //更新LinkedList的实际大小
        size += numNew;
        modCount++;
        return true;
    }

    //清空双向链表数据
    public void clear() {
       //从表头开始遍历清除
       //原理:设置当前节点以及前后节点都为null,再把后一个节点设置为最新节点
        for (Node<E> x = first; x != null;) {
            Node<E> next = x.next;
            x.item = null;
            x.next = null;
            x.prev = null;
            x = next;
        }
        first = last = null;
        //最后更新size设置为0
        size = 0;
        modCount++;
    }

    //获取指定位置的元素
    public E get(int index) {
        checkElementIndex(index);
        return node(index).item;
    }

    //设置index位置的指定值element
    public E set(int index, E element) {
        checkElementIndex(index);
        Node<E> x = node(index);
        E oldVal = x.item;
        x.item = element;
        return oldVal;
    }

   //在index前添加节点,且节点的值为element
    public void add(int index, E element) {
        checkPositionIndex(index);

        if (index == size)
            linkLast(element);
        else
            linkBefore(element, node(index));
    }

    //删除index位置的节点
    public E remove(int index) {
        checkElementIndex(index);
        return unlink(node(index));
    }

    //是否有该元素的索引值
    private boolean isElementIndex(int index) {
        return index >= 0 && index < size;
    }


    //很重要,LinkedList随机访问的原理
     //获取双向链表中指定位置的节点
    Node<E> node(int index) {
        // assert isElementIndex(index);
        //若获取元素的位置小于1/2size时,则从前向后查找
        //反之,从后向前查找
        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;
        }
    }

    // Search Operations

    //获取元素o对应的节点索引值,若不存在返回-1
    //从前向后查找,查找第一个o出现的位置
    public int indexOf(Object o) {
        int index = 0;
        //从表头开始逐个向后查找
        if (o == null) {
            for (Node<E> x = first; x != null; x = x.next) {
                if (x.item == null)
                    return index;
                index++;
            }
        } else {
            for (Node<E> x = first; x != null; x = x.next) {
                if (o.equals(x.item))
                    return index;
                index++;
            }
        }
        return -1;
    }

    //获取元素o对应的节点索引值,若不存在返回-1
    //从后向前查找,查找第一个o出现的位置
    public int lastIndexOf(Object o) {
        int index = size;
        if (o == null) {
            for (Node<E> x = last; x != null; x = x.prev) {
                index--;
                if (x.item == null)
                    return index;
            }
        } else {
            for (Node<E> x = last; x != null; x = x.prev) {
                index--;
                if (o.equals(x.item))
                    return index;
            }
        }
        return -1;
    }

    // Queue operations.

    //获取第一个节点元素,若不存在则返回null,不删除元素
    public E peek() {
        final Node<E> f = first;
        return (f == null) ? null : f.item;
    }

    //获取第一个节点元素,不删除元素
    //若列表为空,则抛出异常
    public E element() {
        return getFirst();
    }

    //获取第一节点元素,同时会删除该元素
    //若列表为空,则返回null
    public E poll() {
        final Node<E> f = first;
        return (f == null) ? null : unlinkFirst(f);
    }

    //获取第一节点元素,同时会删除该元素
    //若列表为空,则抛出异常
    public E remove() {
        return removeFirst();
    }

    //在列表的尾部添加元素e,返回true则成功
    public boolean offer(E e) {
        return add(e);
    }

    // Deque operations
    //在列表最前面插入元素e
    public boolean offerFirst(E e) {
        addFirst(e);
        return true;
    }

    //在列表的尾部插入元素e
    public boolean offerLast(E e) {
        addLast(e);
        return true;
    }

    //获取第一个节点元素,不删除该元素
    //若列表为空,则返回null
    public E peekFirst() {
        final Node<E> f = first;
        return (f == null) ? null : f.item;
    }

    //获取最后一个节点元素,不删除该元素
    //若列表为空,则返回null
    public E peekLast() {
        final Node<E> l = last;
        return (l == null) ? null : l.item;
    }

    //获取第一个节点元素,同时删除该元素
    //若列表为空,则返回null
    public E pollFirst() {
        final Node<E> f = first;
        return (f == null) ? null : unlinkFirst(f);
    }

    //获取最后一个节点元素,同时删除该元素
    //若列表为空,则返回null
    public E pollLast() {
        final Node<E> l = last;
        return (l == null) ? null : unlinkLast(l);
    }

    //将元素推送到由此列表表示的堆栈上。 换句话说,在该列表的前面插入元素。 
    //相当于addFirst(E)
    public void push(E e) {
        addFirst(e);
    }

    //获取第一个节点元素,并删除该元素
    public E pop() {
        return removeFirst();
    }

    //从前向后遍历,删除第一次出现元素o
    public boolean removeFirstOccurrence(Object o) {
        return remove(o);
    }

    //从后向前遍历,删除第一次出现元素o
    public boolean removeLastOccurrence(Object o) {
        if (o == null) {
            for (Node<E> x = last; x != null; x = x.prev) {
                if (x.item == null) {
                    unlink(x);
                    return true;
                }
            }
        } else {
            for (Node<E> x = last; x != null; x = x.prev) {
                if (o.equals(x.item)) {
                    unlink(x);
                    return true;
                }
            }
        }
        return false;
    }

    //返回列表指定index位置起的列表迭代器
    public ListIterator<E> listIterator(int index) {
        checkPositionIndex(index);
        return new ListItr(index);
    }

    …………………………

    //双向链表的节点的数据结构
    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;
        }
    }

    /**
     * @since 1.6
     */
    public Iterator<E> descendingIterator() {
        return new DescendingIterator();
    }

    /**
     * Adapter to provide descending iterators via ListItr.previous
     */
    private class DescendingIterator implements Iterator<E> {
        private final ListItr itr = new ListItr(size());

        public boolean hasNext() {
            return itr.hasPrevious();
        }

        public E next() {
            return itr.previous();
        }

        public void remove() {
            itr.remove();
        }
    }

    @SuppressWarnings("unchecked")
    private LinkedList<E> superClone() {
        try {
            return (LinkedList<E>) super.clone();
        } catch (CloneNotSupportedException e) {
            throw new InternalError(e);
        }
    }

    //克隆一个LinkedList实例,
    public Object clone() {
        LinkedList<E> clone = superClone();

        // 初始化clone的LinkedList的节点
        clone.first = clone.last = null;
        clone.size = 0;
        clone.modCount = 0;

        // 将原列表的元素节点全部赋值给新克隆的列表中
        for (Node<E> x = first; x != null; x = x.next)
            clone.add(x.item);

        return clone;
    }

    //返回一个安全的LinkedList的数组。
    public Object[] toArray() {
        //新建一个Object数组
        Object[] result = new Object[size];
        int i = 0;
        //通过从前向后遍历将LinkedList中的节点元素添加到Object数组中
        for (Node<E> x = first; x != null; x = x.next)
            result[i++] = x.item;
        return result;
    }

     // 返回LinkedList的模板数组。所谓模板数组,即可以将T设为任意的数据类型    
    public <T> T[] toArray(T[] a) {    
        // 若数组a的大小 < LinkedList的元素个数(意味着数组a不能容纳LinkedList中全部元素)    
        // 则新建一个T[]数组,T[]的大小为LinkedList大小,并将该T[]赋值给a。    
        if (a.length < size)    
            a = (T[])java.lang.reflect.Array.newInstance(    
                                a.getClass().getComponentType(), size);    
        // 将链表中所有节点的数据都添加到数组a中    
        int i = 0;    
        Object[] result = a;    
        for (Entry<E> e = header.next; e != header; e = e.next)    
            result[i++] = e.element;    
   
        if (a.length > size)    
            a[size] = null;    
   
        return a;    
    }    

    private static final long serialVersionUID = 876323262645176354L;
// java.io.Serializable的写入函数    
    // 将LinkedList的“容量,所有的元素值”都写入到输出流中    
    private void writeObject(java.io.ObjectOutputStream s)    
        throws java.io.IOException {    
        // Write out any hidden serialization magic    
        s.defaultWriteObject();    
   
        // 写入“容量”    
        s.writeInt(size);    
   
        // 将链表中所有节点的数据都写入到输出流中    
        for (Entry e = header.next; e != header; e = e.next)    
            s.writeObject(e.element);    
    }    
   
    // java.io.Serializable的读取函数:根据写入方式反向读出    
    // 先将LinkedList的“容量”读出,然后将“所有的元素值”读出    
    private void readObject(java.io.ObjectInputStream s)    
        throws java.io.IOException, ClassNotFoundException {    
        // Read in any hidden serialization magic    
        s.defaultReadObject();    
   
        // 从输入流中读取“容量”    
        int size = s.readInt();    
   
        // 新建链表表头节点    
        header = new Entry<E>(null, null, null);    
        header.next = header.previous = header;    
   
        // 从输入流中将“所有的元素值”并逐个添加到链表中    
        for (int i=0; i<size; i++)    
            addBefore((E)s.readObject(), header);    
    }    

}

总结:

  1. LinkedList是基于双表链结构实现的,且head节点上是不存放数据的;其中有个很重要的内部类Node,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;
        }
    }

2. LinkedList提供两个构造函数,一个是无参的构造函数,通过此会创建一个包含head节点空列表;另外一个带Collection参数的构造函数,在创建的LinkedList实例的同时,会把Collection元素添加到列表尾部。

3. 在遍历链表过程中有个加速动作源码中先将index与长度size的一半比较,如果index<size/2,就只从位置0往后遍历到位置index处,而如果index>size/2,就只从位置size往前遍历到位置index处。这样可以减少一部分不必要的遍历,从而提高一定的效率(实际上效率还是很低)。

//很重要,LinkedList随机访问的机制
     //获取双向链表中指定位置的节点
    Node<E> node(int index) {
        // assert isElementIndex(index);
        //若获取元素的位置小于1/2size时,则从前向后查找
        //反之,从后向前查找
        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;
        }
    }

4. LinkedList是基于链表实现的,因此不存在容量不足的问题,所以这里没有扩容的方法。

5. 由于LinkedList实现了Deque,而Deque接口定义了在双端队列两端访问元素的方法。提供插入、移除和检查元素的方法。每种方法都存在两种形式:一种形式在操作失败时抛出异常,另一种形式返回一个特殊值(null 或 false,具体取决于操作。


猜你喜欢

转载自blog.csdn.net/hzw2017/article/details/80358152
今日推荐