Java源码分析之LinkedList

时间:2021-06-11 17:21:45

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会更好。