java容器体系(三)----List(LinkedList)

  LinkedList 是 List 的又一种实现方法,首先看一下它的类图:

 

 

   LinkedList 继承自 AbstractSequentialList, 实现了Deque、Cloneable、Serializable 接口,同 ArrayList 一样,它包含了AbstractList 的所有的行为特征,但它的实现方式是链表,而且是双向链表。

1、成员变量

  LinkedList 提供了四个成员变量

transient int size = 0; // LinkedList 的容量
transient Node<E> first; // 第一个节点
transient Node<E> last; // 最后一个节点
protected transient int modCount = 0; // List结构化修改的次数(size大小发生改变的操作都会增加)

    可以看到,四个变量都使用了 transient 进行修饰,在序列化的时候这三个变量不会序列化。

  Node 是 LinkedList 的私有内部类,实现代码如下:

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、构造函数

public LinkedList() {
}
public LinkedList(Collection<? extends E> c) {
     this();
     addAll(c);
}

  LinkedList 提供了两个构造函数,一个是无参数构造函数;另一个是带有 Collection 类型参数的构造函数。可以看到,与ArrayList不同,LinkedList 并不需要设置初始容量。

3、实现自Deque的方法

  Deque 是一个队列接口,表明该类的实现类是一个双向队列,实现了 Queue。Queue 提供了 add(E e)、offer(E e)、remove()、poll()、element()、peek() 方法,Deque 提供了addFirst(E e)、addLast(E e)、offerFirst(E e)、offerLast(E e)、removeFirst()、removeLast()、pollFirst()、pollLast()、getFirst()、getLast()、peekFirst()、peekLast()等。

  Queue 提供的方法主要是队列头部取出以及尾部的加入方法,是FIFO(先入先出)的数据结构;Deque 在队列的头部和尾部均可以获取、添加、以及删除。下面分析一下具体方法的实现:

  (1)add(E e) 方法

public boolean add(E e) {  // 在尾部添加一个元素,等同于 addLast(E e)
        linkLast(e);
        return true;
}
void linkLast(E e) {
        final Node<E> l = last;
        final Node<E> newNode = new Node<>(l, e, null);
        last = newNode;
        if (l == null) // 加入之前,last 未初始化
            first = newNode; // 初始化 first,此时first = last
        else
            l.next = newNode;
        size++; // 元素数量+1
        modCount++; // 修改次数+1
}

  (2)offer(E e) 方法      其实使用的是add方法

public boolean offer(E e) {
        return add(e);
}

   (3) remove() 方法

public E remove() { // 删除队列的第一个接地那
        return removeFirst();
}
public E removeFirst() {
        final Node<E> f = first;
        if (f == null) // first 未初始化,表示队列里无数据
            throw new NoSuchElementException();
        return unlinkFirst(f);
}
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) // 即此时first=null时,队列里没有元素,last也置为null
            last = null;
        else
            next.prev = null; // 前驱节点释放
        size--; // 队列元素数量-1
        modCount++; // 结构化修改数量+1
        return element;
    }

  (4)poll() 方法

public E poll() { // 删除队列头部的第一个元素并返回
        final Node<E> f = first;
        return (f == null) ? null : unlinkFirst(f);
}

  poll() 方法和 remove() 方法差不多,都是使用 unlinkFirst(E e) 删除头部的第一个元素,但是和 remove(E e) 不同的是,在队列为空的情况下调用 poll() 会返回 null,而 remove() 方法会抛出 NoSuchElementException 异常(其实是使用removeFirst(E e) 方法抛出的异常)。

  (5)element() 方法

public E element() { // 返回头部节点的元素
        return getFirst();
}
public E getFirst() {
        final Node<E> f = first;
        if (f == null) // 队列为空时,抛出异常
            throw new NoSuchElementException();
        return f.item;
}

  (6)peek() 方法

public E peek() {
        final Node<E> f = first;
        return (f == null) ? null : f.item;
}

  peek() 和 element() 方法都是返回队列的头部节点,队列为空时,peek() 会返回 null ,element() 会抛出 NoSuchElementException 异常。

  以上是Queue 提供的方法,其实还有 Collection 当中的方法,后面再介绍,下面介绍 Dueue 的方法。

  (1)addFirst(E e)

public void addFirst(E e) { // 在队列的头部添加元素
        linkFirst(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,first为null
            last = newNode;
        else
            f.prev = newNode;
        size++;
        modCount++;
}

  (2)addLast(E e)

public void addLast(E e) { // 等同于add(E e)和offer(E e),只是没有返回值
        linkLast(e);
}

  (3)offerFirst(E e)

public boolean offerFirst(E e) { // 等同于addFirst(E e),有返回值
        addFirst(e);
        return true;
}

  (4)offerLast(E e)

public boolean offerLast(E e) { // 等同于addLast(E e),有返回值
        addLast(e);
        return true;
}

  (5)push(E e)

public void push(E e) {
        addFirst(e);
}

  (6)pop(E e)

public E pop() { // 删除队列头部元素并返回,队列为空会抛出异常
        return removeFirst();
}

 

4、其他方法

  (1)get(int index)方法

public E get(int index) { // 获取指定位置的元素
        checkElementIndex(index);
        return node(index).item;
}
private void checkElementIndex(int index) {
        if (!isElementIndex(index)) // 判断是否 0<=index<size
            throw new IndexOutOfBoundsException(outOfBoundsMsg(index));
}
private boolean isElementIndex(int index) {
        return index >= 0 && index < size;
}
private String outOfBoundsMsg(int index) {
        return "Index: "+index+", Size: "+size;
}


Node<E> node(int index) { // 这个才是真正的获取指定位置元素的方法,获取的是引用
        // assert isElementIndex(index);

        if (index < (size >> 1)) { // index < size/2,从头部开始遍历,否则从尾部开始遍历
            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;
        }
    }

  (2)set(int index,E element)

public E set(int index, E element) {
        checkElementIndex(index);
        Node<E> x = node(index); // 将获取到指定位置的引用返回
        E oldVal = x.item;
        x.item = element;
        return oldVal;
}

  (3)add(int index, E element)

public void add(int index, E element) {
        checkPositionIndex(index);

        if (index == size)
            linkLast(element);  // 从尾部添加元素
        else
            // 获取指定位置元素的引用,其前驱节点改为新元素
            linkBefore(element, node(index));
}
void linkBefore(E e, Node<E> succ) {
        // assert succ != null;
        final Node<E> pred = succ.prev;
        final Node<E> newNode = new Node<>(pred, e, succ);
        succ.prev = newNode;
        if (pred == null)
            first = newNode;
        else
            pred.next = newNode;
        size++;
        modCount++;
}    

  (4)indexOf(Object o)

public int indexOf(Object o) { // 从头部遍历,返回头一个等于输入参数的元素位置
        int index = 0;
        if (o == null) { // 为 null 时,判断元素等于null,否则调用equals判断元素相等
            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;
}

  (5)itrator() 方法

  LinkedList 中本身没有 itrator() 的方法体,方法体在其父类 AbstractSequentialList 中,它实际使用的也是 AbstractList 的实现。

  (6)listItrator() 方法

  当然 ListedList 作为 AbstractList 的子孙类,同样提供了 listItrator() 方法,既可以向前遍历,也可以向后遍历。其返回类型 Iterator 的实现方式也是内部类:

// 无参数的实现方式在 AbstractList 中已经实现
public ListIterator<E> listIterator(int index) {
        checkPositionIndex(index);
        return new ListItr(index);
}
private class ListItr implements ListIterator<E> {
        private Node<E> lastReturned;
        private Node<E> next;
        private int nextIndex;
        private int expectedModCount = modCount;

        ListItr(int index) {
            // assert isPositionIndex(index);
            next = (index == size) ? null : node(index); // 下一节点位置
            nextIndex = index;
        }

        public boolean hasNext() {
            return nextIndex < size;
        }

        public E next() {
            checkForComodification();
            if (!hasNext())
                throw new NoSuchElementException();

            lastReturned = next;
            next = next.next;
            nextIndex++;
            return lastReturned.item;
        }

        public boolean hasPrevious() {
            return nextIndex > 0;
        }

        public E previous() {
            checkForComodification();
            if (!hasPrevious())
                throw new NoSuchElementException();

            lastReturned = next = (next == null) ? last : next.prev;
            nextIndex--;
            return lastReturned.item;
        }

        public int nextIndex() {
            return nextIndex;
        }

        public int previousIndex() {
            return nextIndex - 1;
        }

        public void remove() {
            checkForComodification(); // 检查expectedModCount 与 modCount 是否相等
            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();  // 检查expectedModCount 与 modCount 是否相等
            lastReturned.item = e;
        }

        public void add(E e) {
            checkForComodification(); // 检查expectedModCount 与 modCount 是否相等
            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();
        }

        final void checkForComodification() {
            if (modCount != expectedModCount)
                throw new ConcurrentModificationException();
        }
}

  (7)descendingIterator() 方法

  descendingIterator() 方法是 LinkedList 特有的方法,该方法可以看做将 itrator() 倒过来,方法名称一样,只是倒序遍历

public Iterator<E> descendingIterator() {
        return new DescendingIterator();
}
// 同样的hasNext()/next()名称,却是由后向前遍历
private class DescendingIterator implements Iterator<E> {
        private final ListItr itr = new ListItr(size());  // LinkedList的内部类
        public boolean hasNext() {
            return itr.hasPrevious();
        }
        public E next() {
            return itr.previous();
        }
        public void remove() {
            itr.remove();
        }
}

  (8)clone() 方法

private LinkedList<E> superClone() {
        try {
            return (LinkedList<E>) super.clone();
        } catch (CloneNotSupportedException e) {
            throw new InternalError(e);
        }
}
public Object clone() {
        LinkedList<E> clone = superClone(); // 感觉可以用new LinkedList()的

        // Put clone into "virgin" state
        clone.first = clone.last = null; // first和last 置为空
        clone.size = 0;
        clone.modCount = 0;

        // 将元素一个一个装入克隆对象
        for (Node<E> x = first; x != null; x = x.next)
            clone.add(x.item);

        return clone;
}

  (9)序列化和反序列化方法

  前面介绍过 LinkedList 的四个成员变量 first、last、size、modCount 都使用了 transient 修饰,无法被序列化。java 对它们进行了重写。

private void writeObject(java.io.ObjectOutputStream s)
        throws java.io.IOException {
        // Write out any hidden serialization magic
        s.defaultWriteObject();

        // Write out size
        s.writeInt(size); // 将 size 写出

        // Write out all elements in the proper order.
        for (Node<E> x = first; x != null; x = x.next)
            s.writeObject(x.item); // 一个接一个将元素写出
}

private void readObject(java.io.ObjectInputStream s)
        throws java.io.IOException, ClassNotFoundException {
        // Read in any hidden serialization magic
        s.defaultReadObject();

        // Read in size
        int size = s.readInt(); // 这里可没有对成员变量size赋值

        // Read in all elements in the proper order.
        for (int i = 0; i < size; i++)
            linkLast((E)s.readObject()); // 内部会进行modCount++,size++操作,因此 modCount=size
}

 

posted @ 2020-09-09 23:40  Sunrise2750  阅读(223)  评论(0)    收藏  举报