线性表之双向链表的实现

模拟LinkList

Posted by HuangCanCan on September 8, 2019

前言

主要是模拟Java中的LinkList的实现。

LinkedList概述

LinkedList是一种双向链表。

根据双向链表的特点,会有头节点和尾节点,并且节点之间是通过前驱指针和后继指针来维护关系的,而不是像数组那样通过位置下标来维护节点间关系的。所以既可以从头到尾遍历,又可以从尾到头遍历。

双向链表是包含两个指针的,pre指向前一个节点,next指向后一个节点,但是第一个节点head的pre指向null,最后一个节点的tail的next指向null。

LinkedList是一个继承于AbstractSequentialList,并实现了List接口和Deque接口的双端链表。

LinkedList底层的链表结构使它支持高效的插入和删除操作,另外它实现了Deque接口,使得LinkedList类也具有队列的特性(addFirst(),removeLast()…),它可以被当作堆栈、队列或双端队列进行操作。

LinkedList 实现了Cloneable接口,即覆盖了函数clone(),能克隆。

public class LinkedList<E>
    extends AbstractSequentialList<E>
    implements List<E>, Deque<E>, Cloneable, java.io.Serializable{
    ...
    }

LinkedList不是线程安全的,如果想使LinkedList变成线程安全的,可以调用静态类Collections类中的synchronizedList方法。

和ArrayList一样,类中的iterator()方法和listIterator()方法返回的iterators迭代器是fail-fast的:当某一个线程A通过iterator去遍历某集合的过程中,若该集合的内容被其他线程所改变了;那么线程A访问集合时,就会抛出ConcurrentModificationException异常,产生fail-fast事件,底层由modCount实现。

AbstractSequentialList 实现了get(int index)、set(int index, E element)、add(int index, E element) 和 remove(int index)这些骨干性函数。降低了List接口的复杂度。这些接口都是随机访问List的,LinkedList是双向链表(链表数据结构本身不具备随机访问的特性);既然它继承于AbstractSequentialList,就相当于已经实现了“get(int index)这些接口”。

此外,我们若需要通过AbstractSequentialList自己实现一个列表,只需要扩展此类,并提供 listIterator() 和 size() 方法的实现即可。若要实现不可修改的列表,则需要实现列表迭代器的 hasNext、next、hasPrevious、previous 和 index 方法即可。

模拟LinkedList

Queue

/**
 * @ClassName Queue
 * @Description 队列
 * @Author HuangCanCan
 * @Date 2019/10/10 17:46
 * @Version 1.0
 **/
public interface Queue {

    /**
     * 将指定的元素值e插入此列表末尾
     *
     * @param e
     * @return
     */
    boolean offer(Object e);

    /**
     * 获取并移除此队列的头,如果此队列为空,则抛出NoSuchElementException异
     *
     * @return
     */
    Object remove();

    /**
     * 获取并移除此队列的头,如果此队列为空,则返回 null
     *
     * @return
     */
    Object poll();

    /**
     * 获取但不移除此队列的头;如果此队列为空,则抛出NoSuchElementException异常
     *
     * @return
     */
    Object element();

    /**
     * 获取但不移除此队列的头;如果此队列为空,则返回 null
     *
     * @return
     */
    Object peek();
}

Deque

/**
 * @ClassName Deque
 * @Description 双端队列
 * @Author HuangCanCan
 * @Date 2019/10/10 18:23
 * @Version 1.0
 **/
public interface Deque extends Queue {
    /**
     * 将指定的元素插入此双端队列的开头
     *
     * @param e
     * @return
     */
    boolean offerFirst(Object e);

    /**
     * 将指定的元素插入此双端队列的末尾
     *
     * @param e
     * @return
     */
    boolean offerLast(Object e);

    /**
     * 获取,但不移除此双端队列的第一个元素;如果此双端队列为空,则返回 null
     *
     * @return
     */
    Object peekFirst();

    /**
     * 获取,但不移除此双端队列的最后一个元素;如果此双端队列为空,则返回 null
     *
     * @return
     */
    Object peekLast();

    /**
     * 获取并移除此双端队列的第一个元素;如果此双端队列为空,则返回 null
     *
     * @return
     */
    Object pollFirst();

    /**
     * 获取并移除此双端队列的最后一个元素;如果此双端队列为空,则返回 null
     *
     * @return
     */
    Object pollLast();

    /**
     * 将一个元素推入此双端队列所表示的堆栈(换句话说,此双端队列的头部)
     *
     * @param e
     */
    void push(Object e);

    /**
     * 从此双端队列所表示的堆栈中弹出一个元素(换句话说,移除并返回此双端队列的头部)
     */
    Object pop();

    /**
     * 从此双端队列移除第一次出现的指定元素,如果列表中不包含次元素,则没有任何改变
     *
     * @param e
     * @return
     */
    boolean removeFirstOccurrence(Object e);

    /**
     * 从此双端队列移除最后一次出现的指定元素,如果列表中不包含次元素,则没有任何改变
     *
     * @param e
     * @return
     */
    boolean removeLastOccurrence(Object e);
}

LinkedList

import java.util.Collection;
import java.util.Iterator;
import java.util.NoSuchElementException;

/**
 * @ClassName LinkedList
 * @Description 模拟LinkedList
 * @Author HuangCanCan
 * @Date 2019/10/8 17:18
 * @Version 1.0
 **/
public class LinkedList implements List, Iterable, Deque {

    /**
     * 指向头结点,
     * <p>
     * 不变的: (first == null && last == null) || (first.prev == null && first.data != null)
     */
    private Node first;

    /**
     * 指向尾结点
     * <p>
     * 不变的: (first == null && last == null) || (last.next == null && last.data != null)
     */
    private Node last;

    /**
     * 结点个数
     */
    private int size;

    /**
     * 修改此列表的次数
     * <p>
     * 用于在迭代器遍历时,用于判断在迭代过程中是否发生了修改操作,如果发生了修改,则抛出ConcurrentModificationException异常
     */
    private int modCount;

    public LinkedList() {
    }

    public LinkedList(Collection c) {
        this();
        addAll(c);
    }

    @Override
    public int size() {
        return size;
    }

    @Override
    public Object get(int index) {
        //检查index范围
        checkElementIndex(index);

        return getNode(index).data;
    }

    /**
     * 判断元素的索引是否越界
     *
     * @param index
     */
    private void checkElementIndex(int index) {
        if (index < 0 || index > size) {
            throw new RuntimeException("指针越界异常:" + index);
        }
    }

    @Override
    public boolean isEmpty() {
        return size == 0;
    }

    @Override
    public boolean contains(Object e) {
//        判断列表中是否包含有元素值e,返回true当列表中至少存在一个元素值e
        return indexOf(e) >= 0;
    }

    /**
     * 正向查找,返回LinkedList中元素值Object data <strong>第一次</strong> 出现的位置,如果元素不存在,则返回-1
     *
     * @param data
     * @return
     */
    @Override
    public int indexOf(Object data) {
        //返回第一次出现的指定元素的索引
        int index = 0;
        if (data == null) {
            for (Node x = first; x != null; x = x.next) {
                if (x.data == null) {
                    return index;
                }
                index++;
            }
        } else {
            for (Node x = first; x != null; x = x.next) {
                if (data.equals(x.data)) {
                    return index;
                }
                index++;
            }
        }
        return -1;
    }

    /**
     * 逆向查找,返回LinkedList中元素值Object e <strong>最后一次</strong>出现的位置,如果元素不存在,则返回-1
     *
     * @param e
     * @return
     */
    public int lastIndexOf(Object e) {
        int index = size;
        if (e == null) {
            for (Node x = last; x != null; x = x.pre) {
                index--;
                if (x.data == null) {
                    return index;
                }
            }
        } else {
            for (Node x = last; x != null; x = x.pre) {
                index--;
                if (e.equals(x.data)) {
                    return index;
                }
            }
        }
        return -1;
    }

    @Override
    public void add(int index, Object data) {
        //检查index范围
        checkElementIndex(index);

        /*
          判断此索引是否等于节点的个数,如果相等那就在尾部插入元素,否则在中间插入元素
         */
        if (index == size) {
            //尾插入
            linkLast(data);
        } else {
            //中间插入
            //获取指定索引位置的节点
            Node node = getNode(index);
            //在指定索引位置的节点前插入新节点
            linkBefore(data, node);
        }

    }

    /**
     * 在非空节点node之前插入新节点的值。中间操作
     *
     * @param data 新节点的值
     * @param node 当前节点
     */
    private void linkBefore(Object data, Node node) {
        Node pred = node.pre;
        // 构建一个新的节点
        Node newNode = new Node(pred, data, node);
        //设置node的前驱节点为这个新的节点
        node.pre = newNode;
//        第一个节点head的pre指向null,最后一个节点的tail的next指向null
        //判断node是否是首节点(node.pre前驱节点为null)
        if (pred == null) {
            //表示node是首节点
            //将newNode设置为首节点
            first = newNode;
        } else {
            //node不是首节点,设置node的前驱节点的后续节点设置为新的节点
            pred.next = newNode;
        }
        //节点个数加 1
        size++;
        //修改此列表的次数加 1
        modCount++;
    }

    /**
     * 将新节点的值添加到链表尾部。尾插入
     *
     * @param data
     */
    private void linkLast(Object data) {
        Node l = last;
        //构建一个新的节点
        Node newNode = new Node(l, data, null);
        //将新节点作为尾节点
        last = newNode;
        //如果原尾节点为null,即原链表为null,则链表首节点设置为newNode
        if (l == null) {
            first = newNode;
        } else {
            //否则,原尾节点的next设置为newNode
            l.next = newNode;
        }
        //节点个数加 1
        size++;
        //修改此列表的次数加 1
        modCount++;
    }

    /**
     * 在链表头部添加元素,头插入
     *
     * @param data
     */
    private void linkFirst(Object data) {
        Node f = first;
        // 构建一个新节点
        Node newNode = new Node(null, data, f);
        //将新节点作为首节点
        first = newNode;

        //如果原首节点为null,即原链表为null,则链表尾节点也设置为newNode
        if (f == null) {
            last = newNode;
        } else {
            //否则,原首节点的pre设置为newNode
            f.pre = newNode;
        }
        //节点个数加 1
        size++;
        //修改此列表的次数加 1
        modCount++;
    }


    /**
     * 返回指定索引位置的节点
     *
     * @return
     */
    private Node getNode(int index) {
        /*
         * *************【折半思想】**************
         * 当index < size/2 (size >> 1)时,从列表首节点向后查找
         * 当 index >= size/2时,从列表尾节点向前查找
         */
        if (index < size / 2) {
            Node x = first;
            for (int i = 0; i < index; i++) {
                x = x.next;
            }
            return x;
        } else {
            Node x = last;
            for (int i = size - 1; i > index; i--) {
                x = x.pre;
            }
            return x;
        }

    }

    @Override
    public void add(Object data) {
        linkLast(data);
    }

    /**
     * 按照指定collection的迭代器所返回的元素顺序,将该collection中的所有元素添加到此链表的尾部。尾部插入
     * <p>
     * 如果指定的集合添加到链表的尾部的过程中,集合被修改,则该插入过程的后果是不确定的。
     * 一般这种情况发生在指定的集合为该链表的一部分,且其非空。
     *
     * @param c
     */
    public boolean addAll(Collection c) {
        return addAll(size, c);
    }

    /**
     * 从指定的位置开始,将指定collection中的所有元素插入到此链表中,新元素的顺序为指定collection的迭代器所返回的元素顺序
     *
     * @param index
     * @param c
     */
    public boolean addAll(int index, Collection c) {
        //检查index范围
        checkElementIndex(index);

        Object[] a = c.toArray();
        int numNew = a.length;
        if (numNew == 0) {
            return false;
        }

        Node pred, //指向需要插入节点的位置的前驱节点
                succ;//需要插入节点的位置
        if (index == size) {
            // 在链表表尾部插入集合元素
            succ = null;
            pred = last;

        } else {
            // 在中间插入
            // 获取指定索引位置的节点
            succ = getNode(index);
            pred = succ.pre;
        }

        //指定collection中的所有元素依次插入到此链表中指定位置的过程
        for (Object data : a) {
            // 构建新节点
            Node newNode = new Node(pred, data, null);
            if (pred == null) { // 如果原链表为null,则新插入的节点作为链表首节点
                first = newNode;
            } else {
                pred.next = newNode;
            }
            pred = newNode;  // pred指针向后移动,指向下一个需插入节点位置的前一个节点
        }

        //集合元素插入完成后,与原链表index位置后面的子链表链接起来
        if (succ == null) { //说明之前是在列表尾部插入的集合元素
            last = pred;  //pred指向的是最后插入的那个节点
        } else {
            // 否则,是在中间插入的集合元素
            pred.next = succ;
            succ.pre = pred;
        }

        size += numNew;
        modCount++;
        return true;

    }

    /**
     * 在链表头部添加元素,头插入
     *
     * @param data
     */
    public void addFirst(Object data) {
        linkFirst(data);
    }

    /**
     * 在链表尾部添加元素,等价于add
     *
     * @param data
     */
    public void addLast(Object data) {
        linkLast(data);
    }

    @Override
    public boolean addBefore(Object obj, Object e) {
        int index = indexOf(obj);
        if (index < 0) {
            return false;
        }
        add(index, e);
        return true;
    }

    @Override
    public boolean addAfter(Object obj, Object e) {
        int index = indexOf(obj);
        if (index < 0) {
            return false;
        }
        add(index + 1, e);
        return true;
    }

    @Override
    public Object remove(int index) {
        checkElementIndex(index);

        Node oldNode = getNode(index); // 获取指定索引处的节点,老节点
        return unlink(oldNode);
    }

    /**
     * 删除非空节点node
     *
     * @param node 要删除的节点
     * @return
     */
    private Object unlink(Node node) {
        Object oldValue = node.data;
        Node prev = node.pre; //老节点的前驱节点
        Node next = node.next; // 老节点的后续节点

//        更改老节点的前驱节点和后续节点的的关联
//        prev.next = next;
//        next.pre = prev;

        /*
            需要判断删除的元素是否是头节点和尾节点
         */
        if (prev == null) { //代表该节点是头节点
            first = next;
        } else {    // 不是头结点
            prev.next = next;
            node.pre = null;
        }

        if (next == null) { //代表该节点是尾节点
            last = prev;
        } else { // 不是尾结点
            next.pre = prev;
            node.next = null;
        }

        node.data = null; // 使GC能够快速回收

        size--;
        modCount++;

        return oldValue;
    }

    @Override
    public boolean remove(Object e) {
        //移除列表中首次出现的指定元素(如果存在),LinkedList中允许存放重复的元素
        /*
         * 第一种:
         */
//        int index = indexOf(e);//返回元素e第一次出现的索引
//        if (index < 0) {
//            return false;
//        }
//        remove(index);
//        return true;

        if (e == null) {
            for (Node x = first; x != null; x = x.next) {
                if (x.data == null) {
                    unlink(x);
                    return true;
                }
            }
        } else {
            for (Node x = first; x != null; x = x.next) {
                if (e.equals(x.data)) {
                    unlink(x);
                    return true;
                }
            }
        }
        return false;
    }

    /**
     * 替换指定索引位置节点的元素值,并返回旧值
     *
     * @param index   索引
     * @param element 元素的值
     * @return
     */
    private Object set(int index, Object element) {
        checkElementIndex(index);
        Node node = getNode(index);
        Object oldValue = node.data;
        node.data = element;
        return oldValue;
    }

    @Override
    public Object replace(int index, Object e) {
        return set(index, e);
    }

    @Override
    public String toString() {
        if (size == 0) {
            return "[]";
        }
        StringBuilder builder = new StringBuilder("[");
        Node p = first;
        for (int i = 0; i < size; i++) {
            if (i != size - 1) {
                builder.append(p.data + ",");
            } else {
                builder.append(p.data);
            }

            //移动指针到下一个结点
            p = p.next;
        }

        builder.append("]");
        return builder.toString();
    }

    @Override
    public Iterator iterator() {

        return new LinkedListIteraor();
    }

    /**
     * 获取列表首节点元素值
     */
    public Object getFirst() {
        Node head = first;
        if (head == null) {
            throw new NoSuchElementException();
        }
        return head.data;
    }

    /**
     * 获取列表尾节点元素值
     */
    public Object getLast() {
        Node tail = last;
        if (tail == null) {
            throw new NoSuchElementException();
        }
        return tail.data;
    }

    /**
     * 移除首节点,并返回该节点的元素值
     *
     * @return
     */
    public Object removeFirst() {
        final Node f = first;
        if (f == null)
            throw new NoSuchElementException();
        return unlinkFirst(f);
    }

    /**
     * 移除尾节点,并返回该节点的元素值
     *
     * @return
     */
    public Object removeLast() {
        final Node l = last;
        if (l == null)
            throw new NoSuchElementException();
        return unlinkLast(l);
    }

    /**
     * 删除非空的首节点f
     */
    private Object unlinkFirst(Node f) {
        Object element = f.data;
        Node next = f.next;
        f.data = null;
        f.next = null; // help GC
        first = next; //将原首节点的next节点设置为首节点
        if (next == null)  //如果原链表只有一个节点,即原首节点,删除后,链表为null
            last = null;
        else
            next.pre = null;
        size--;
        modCount++;
        return element;
    }

    //删除非空的尾节点l
    private Object unlinkLast(Node l) {
        // assert l == last && l != null;
        final Object element = l.data;
        final Node prev = l.pre;
        l.data = null;
        l.pre = null; // help GC
        last = prev; //将原尾节点的prev节点设置为尾节点
        if (prev == null) //如果原链表只有一个节点,则删除后,链表为null
            first = null;
        else
            prev.next = null;
        size--;
        modCount++;
        return element;
    }

    @Override
    public boolean offer(Object e) {
//        将指定的元素值e插入此列表末尾
        add(e);
        return true;
    }

    @Override
    public Object remove() {
        //获取并 移除 此队列的头,如果此队列为空,则抛出NoSuchElementException异
        return removeFirst();
    }

    @Override
    public Object poll() {
        //获取并 移除 此队列的头,如果此队列为空,则返回 null
        Node f = first;
        return f == null ? null : unlinkFirst(f);
    }

    @Override
    public Object element() {
        //获取但 不移除 此队列的头;如果此队列为空,则抛出NoSuchElementException异常
        return getFirst();
    }

    @Override
    public Object peek() {
        //获取但 不移除 此队列的头;如果此队列为空,则返回 null
        Node f = first;
        return f == null ? null : f.data;
    }

    @Override
    public boolean offerFirst(Object e) {
        addFirst(e);
        return true;
    }

    @Override
    public boolean offerLast(Object e) {
        addLast(e);
        return true;
    }

    @Override
    public Object peekFirst() {
        final Node f = first;
        return (f == null) ? null : f.data;
    }

    @Override
    public Object peekLast() {
        final Node l = last;
        return (l == null) ? null : l.data;
    }

    @Override
    public Object pollFirst() {
        final Node f = first;
        return (f == null) ? null : unlinkFirst(f);
    }

    @Override
    public Object pollLast() {
        final Node l = last;
        return (l == null) ? null : unlinkLast(l);
    }

    @Override
    public void push(Object e) {
        addFirst(e);
    }

    @Override
    public Object pop() {
        return removeFirst();
    }

    @Override
    public boolean removeFirstOccurrence(Object e) {
        return remove(e);
    }

    @Override
    public boolean removeLastOccurrence(Object e) {
        if (e == null) {
            for (Node x = last; x != null; x = x.pre) { //逆向向前
                if (x.data == null) {
                    unlink(x);
                    return true;
                }
            }
        } else {
            for (Node x = last; x != null; x = x.pre) {
                if (e.equals(x.data)) {
                    unlink(x);
                    return true;
                }
            }
        }
        return false;
    }

    public static class Node {
        /**
         * 要存储的数据
         */
        Object data;
        /**
         * 后续结点
         */
        Node next;
        /**
         * 前驱结点
         */
        Node pre;

        public Node(Node pre, Object data, Node next) {
            this.pre = pre;
            this.data = data;
            this.next = next;
        }
    }

    private class LinkedListIteraor implements Iterator {

        /**
         * 起始位置的后续节点
         */
        private Node current = first.next;
        /**
         * 记录此刻集合修改的总次数,之后会拿modCount再和此值作比较,如果不相等,说明在迭代过程中,集合发生了修改操作,则会抛出ConcurrentModification异常
         */
        private int expectedModCount = modCount;

        /**
         * 判断是否可以向后移动指针
         */
        private boolean canMove = false;

        @Override
        public boolean hasNext() {
            //后续节点与尾节点比较,如果不相等证明还有元素没有迭代完
            return current != last;
        }

        @Override
        public Object next() {
            if (expectedModCount != modCount) {
                throw new java.util.ConcurrentModificationException();
            }
            if (!hasNext()) {
                throw new java.util.NoSuchElementException();
            }
            Object nextVal = current.data;
            current = current.next;
            canMove = true;
            return nextVal;
        }

        @Override
        public void remove() {
            if (expectedModCount != modCount) {
                throw new java.util.ConcurrentModificationException();
            }
            if (!canMove) {
                throw new IllegalStateException();
            }

            LinkedList.this.unlink(current.pre);

            canMove = false;
            expectedModCount++;
        }

    }
}




遍历集合时进行修改或删除操作为什么会抛出ConcurrentModificationException?

如一下代码:以上面的代码为例

LinkedList linkedList = new LinkedList();
linkedList.add(1);
linkedList.add(2);
linkedList.add(3);
linkedList.add(4);
linkedList.add(5);
linkedList.add(6);
 for (Object i : linkedList) {
 	if ((Integer) i == 2) {
 		linkedList.remove(2);
 	}
 }

输出:Exception in thread "main" java.util.ConcurrentModificationException

原因:

因为增强的for循环内部使用的是iterator迭代器的方式进行遍历的,在遍历过程中,如果进行修改操作会导致exceptedModCount的值和modCount的值不相等.因此会抛出ConcurrentModificationException的异常。

正确的删除元素的方式应该是使用iterator()的方式遍历并进行删除操作,因为迭代器中的remove()方法和集合中的remove()有一点不同就是,前者删除后,会进行exceptedModCount++,因为不会抛出上面那个异常。

下面的代码不会抛出ConcurrentModificationException:

 Iterator iterator = linkedList.iterator();
 while (iterator.hasNext()) {
     Integer next = (Integer) iterator.next();
     if (next == 2) {
     	iterator.remove();
     }
 }

总结

LinkedList是基于双向链表实现的,适合增删多查找少的场景。

LinkedList添加的元素,取时与添加时的顺序一致。因为向双向链表的尾部添加元素,然后按照头节点顺序遍历获取,所以一致。

LinkedList允许添加重复元素。

LinkedList不是线程安全的集合。

LinkedList允许添加null元素。

add方法插入元素是在双向链表的尾部插入。

get方法遍历双向链表,先判断下标靠近头节点还是尾节点,这样会减少多余的循环。

折半思想:当index < size/2 (size » 1)时,从列表首节点向后查找;当 index >= size/2时,从列表尾节点向前查找