Java源码分析之LinkedList

Java源码分析之LinkedList

1.这个类位于java.util包下。

2.继承关系

<span style="font-family:Microsoft YaHei;font-size:14px;">public class LinkedList<E>
    extends AbstractSequentialList<E>
    implements List<E>, Deque<E>, Cloneable, java.io.Serializable</span>

1)继承AbstractSequentialList类,提供了List接口骨干性的实现,并且减少实现List接口的复杂度。

2)实现List接口

3)实现Deque接口,定义双端操作的方法

4)实现Coloneable接口,标记接口

5)实现Serializable接口,标记接口,表示可序列化

3.底层使用双向链表实现的,所以插入和删除操作比ArrayList高效

4.源码分析

1)定义了三个属性

<span style="font-family:Microsoft YaHei;font-size:14px;">    transient int size = 0;//表示的是链表的大小
    transient Node<E> first;//指向第一个结点
    transient Node<E> last;//指向最后一个结点</span>
Node结点的定义:是LinkedList的静态内部类,每个节点指向前一个结点和后一个结点,这就是双向链表结点。
<span style="font-family:Microsoft YaHei;font-size:14px;">    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;
        }
    }</span>

2)构造方法

<span style="font-family:Microsoft YaHei;">  <span style="font-size:14px;">  <span style="font-weight: normal;">public LinkedList() {
    }</span></span></span>

    <span style="font-weight: normal;">public LinkedList(Collection<? extends E> c) {
        this();
        addAll(c);
    }</span>

LinkedList类提供了两个构造方法。一个是无参的构造方法,是一个空链表,第二个是接收一个Collection参数c,先调用自身的无参构造方法,然后调用addAll()方法,将c中的元素加入到链表中。

<span style="font-size:18px;">    public boolean addAll(Collection<? extends E> c) {
        return addAll(size, c);
    }</span>
<span style="font-size:18px;"></span><pre name="code" class="java">    public boolean addAll(int index, Collection<? extends E> c) {
        checkPositionIndex(index); //判断插入的位置index是否在正常范围内0<=index<size
        Object[] a = c.toArray();
        int numNew = a.length;
        if (numNew == 0)//插入的为空的集合
            return false;
        Node<E> pred, succ; //定义两个节点
        if (index == size) { //插入的位置在最后
            succ = null;
            pred = last;
        } else { //插入的位置在中间
            succ = node(index);
            pred = succ.prev;
        }
        for (Object o : a) {
            E e = (E) o;
            Node<E> newNode = new Node<>(pred, e, null); 
            if (pred == null)插入在第0个
                first = newNode;
            else
                pred.next = newNode;
            pred = newNode;
        }
        if (succ == null) {插入的位置在最后面
            last = pred; //last指向最后一个节点
        } else {
            pred.next = succ;
            succ.prev = pred;
        }
        size += numNew; //增加size的大小
        modCount++;
        return true;
    }
 
   

3)链表遍历器:ListIterator

通过调用对象的listIterator(int index)返回从指定位置开始的链表遍历器。
实现了ListIterator接口的类,需要实现hasNext(), next(),hasPrevious(),nextIndex(),previousIndex(),remove(),set(),add()方法。
<span style="font-size:18px;"> public ListIterator<E> listIterator(int index) {
        checkPositionIndex(index);//判断index是否越界
        return new ListItr(index);//返回从指定位置开始的链表遍历器
    }

    private class ListItr implements ListIterator<E> {
        private Node<E> lastReturned = null;
        private Node<E> next;
        private int nextIndex;
        private int expectedModCount = modCount;
</span>
<span>	</span>//构造函数,初始化next和nextIndex
        ListItr(int index) {
            // assert isPositionIndex(index);
            next = (index == size) ? null : node(index);
            nextIndex = index;
        }
<span style="white-space:pre">	</span>//判断是否还有下一个结点
        public boolean hasNext() {
            return nextIndex < size;
        }
<span style="white-space:pre">	</span>//返回下一个结点
        public E next() {
            checkForComodification();
            if (!hasNext())
                throw new NoSuchElementException();

            lastReturned = next;
            next = next.next;
            nextIndex++;
            return lastReturned.item;
        }
<span style="white-space:pre">	</span>//判断是否有前一个结点
        public boolean hasPrevious() {
            return nextIndex > 0;
        }
<span style="white-space:pre">	</span>//返回前一个结点
        public E previous() {
            checkForComodification();
            if (!hasPrevious())
                throw new NoSuchElementException();

            lastReturned = next = (next == null) ? last : next.prev;
            nextIndex--;
            return lastReturned.item;
        }
<span style="white-space:pre">	</span>//
        public int nextIndex() {
            return nextIndex;
        }
<span style="white-space:pre">	</span>//
        public int previousIndex() {
            return nextIndex - 1;
        }
<span style="white-space:pre">	</span>//删除结点
        public void remove() {
            checkForComodification();
            if (lastReturned == null)
                throw new IllegalStateException();

            Node<E> lastNext = lastReturned.next;
            unlink(lastReturned);
            if (next == lastReturned)
                next = lastNext;
            else
                nextIndex--;
            lastReturned = null;
            expectedModCount++;
        }

        public void set(E e) {
            if (lastReturned == null)
                throw new IllegalStateException();
            checkForComodification();
            lastReturned.item = e;
        }
<span style="white-space:pre">	</span>//添加节点
        public void add(E e) {
            checkForComodification();
            lastReturned = null;
            if (next == null)
                linkLast(e);
            else
                linkBefore(e, next);
            nextIndex++;
            expectedModCount++;
        }

        public void forEachRemaining(Consumer<? super E> action) {
            Objects.requireNonNull(action);
            while (modCount == expectedModCount && nextIndex < size) {
                action.accept(next.item);
                lastReturned = next;
                next = next.next;
                nextIndex++;
            }
            checkForComodification();
        }
<span style="white-space:pre">	</span>//与ArrayList类似,也是通过一个属性modCount来判断是否在取得遍历器之后对链表进行结构变化的操作,如果有
<span style="white-space:pre">	</span>//则会抛异常
        final void checkForComodification() {
            if (modCount != expectedModCount)
                throw new ConcurrentModificationException();
        }
    }

4)从后往前遍历

调用descendingIterator()得到LinkedList对象的从后往前的遍历器。
    /**
     * @since 1.6
     */
    public Iterator<E> descendingIterator() {
        return new DescendingIterator();
    }
LinkedList类的遍历器类是通过内部的私有类实现Iterator接口得到的,实现了Iterator接口的类需要自己实现三个方法,hasNext(),next(),remove()。
    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();
        }
    }


5)toArray(T[] a),通过数组的反射机制新建新的数组,在执行toarray操作。

    public <T> T[] toArray(T[] a) {
        if (a.length < size)
           <span style="color:#ff0000;"><strong><u> a = (T[])java.lang.reflect.Array.newInstance(
                                a.getClass().getComponentType(), size);</u></strong></span>
        int i = 0;
        Object[] result = a;
        for (Node<E> x = first; x != null; x = x.next)
            result[i++] = x.item;

        if (a.length > size)
            a[size] = null;

        return a;
    }

5)Java1.8新加入API的spliterator(),下次再说。。。

5.LinkedList与ArrayList的区别:

1)对两者而言在列表末尾增加一个元素所花的开销是固定的,对ArrayList而言,主要的是在内部数组中增加一项,指向所添加的元素,偶尔可能会导致对数组的大小重新分配;对LinkedList而言,这个开销是统一的,分配一

Node节点。

2)在ArrayList中index位置插入或删除一个元素意味着这个列表中index之后元素都被会移动(向后或者向前);而

ArrayList中index位置插入或删除一个元素意味着这个列表中index之后元素都被会移动(向后或者向前);而在

LinkedList中插入或删除一个元素的开销是固定的。

3)由于ArrayList底层是基于数组实现的,所以支持高效的随机访问,而对于LinkedList而言,底层是基于双向链表

实现的,不支持高效的随机访问。

4)ArrayList的空间浪费主要体现在list列表的结尾预留一定的容量空间,而LinkedList的空间花费在每一个元素都消

耗相当的空间。

结论:ArrayList和LinkedList各有优缺点,所以在设计时,如果这个List经常查找元素,则设计成ArrayList,可以提

供更好的性能,如果经常的插入删除元素,则设计成LinkedList会更好。


















评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值