数据结构-双向链表

为了解决对链表操作的灵活性,把单链表的单一指向改为双向驱动,从而形成双向链表。JavaLinkedList就是双向链表的实现,但是因为有双端队列的成分,显得有些不单纯。

 

双向链表的节点定义

 

    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;

        }

    }

 

这一节点定义的变化提高了对链表操作的性能,下面举一个例子:

 

    /**

     * Inserts element e before non-null Node succ.

     */

    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++;

    }

 

因为相邻前节点的信息已经被存储,可以直接通过这一信息获得前一个数据元素,而不是像单链表那样每次都从头再来。虽然花费了内存,但是提高了性能,从而节约了时间。有时候内存和性能是个平衡点的问题。

 

在这里,就不整篇幅的列举代码了,以下亮点特别说明一下。

二分查找的应用:

 

    /**

     * Returns the (non-null) Node at the specified element index.

     */

    Node<E> node(int index) {

        // assert isElementIndex(index);

 

        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;

        }

    }

接近前端从前开始,接近后端从后开始,比单链表显得“工业化”了些。

 

1.8版增加了spliterator处理,在将来spliterator会有很广泛的应用的。

 

    /**

     * Creates a <em><a href="Spliterator.html#binding">late-binding</a></em>

     * and <em>fail-fast</em> {@link  Spliterator} over the elements in this

     * list.

     *

     * <p>The {@code  Spliterator} reports {@link  Spliterator#SIZED} and

     * {@link  Spliterator#ORDERED}.  Overriding implementations should document

     * the reporting of additional characteristic values.

     *

     * @implNote

     * The {@code Spliterator} additionally reports {@link Spliterator#SUBSIZED}

     * and implements {@code trySplit} to permit limited parallelism..

     *

     * @return a {@code Spliterator} over the elements in this list

     * @since 1.8

     */

    @Override

    public Spliterator<E> spliterator() {

        return new LLSpliterator<E>(this, -1, 0);

    }

 

    /** A customized variant of Spliterators.IteratorSpliterator */

    static final class LLSpliterator<E> implements Spliterator<E> {

        static final int BATCH_UNIT = 1 << 10;  // batch array size increment

        static final int MAX_BATCH = 1 << 25;  // max batch array size;

        final LinkedList<E> list; // null OK unless traversed

        Node<E> current;      // current node; null until initialized

        int est;              // size estimate; -1 until first needed

        int expectedModCount; // initialized when est set

        int batch;            // batch size for splits

 

        LLSpliterator(LinkedList<E> list, int est, int expectedModCount) {

            this.list = list;

            this.est = est;

            this.expectedModCount = expectedModCount;

        }

 

        final int getEst() {

            int s; // force initialization

            final LinkedList<E> lst;

            if ((s = est) < 0) {

                if ((lst = list) == null)

                    s = est = 0;

                else {

                    expectedModCount = lst.modCount;

                    current = lst.first;

                    s = est = lst.size;

                }

            }

            return s;

        }

 

        public long estimateSize() { return (long) getEst(); }

 

        public Spliterator<E> trySplit() {

            Node<E> p;

            int s = getEst();

            if (s > 1 && (p = current) != null) {

                int n = batch + BATCH_UNIT;

                if (n > s)

                    n = s;

                if (n > MAX_BATCH)

                    n = MAX_BATCH;

                Object[] a = new Object[n];

                int j = 0;

                do { a[j++] = p.item; } while ((p = p.next) != null && j < n);

                current = p;

                batch = j;

                est = s - j;

                return Spliterators.spliterator(a, 0, j, Spliterator.ORDERED);

            }

            return null;

        }

 

        public void forEachRemaining(Consumer<? super E> action) {

            Node<E> p; int n;

            if (action == null) throw new NullPointerException();

            if ((n = getEst()) > 0 && (p = current) != null) {

                current = null;

                est = 0;

                do {

                    E e = p.item;

                    p = p.next;

                    action.accept(e);

                } while (p != null && --n > 0);

            }

            if (list.modCount != expectedModCount)

                throw new ConcurrentModificationException();

        }

 

        public boolean tryAdvance(Consumer<? super E> action) {

            Node<E> p;

            if (action == null) throw new NullPointerException();

            if (getEst() > 0 && (p = current) != null) {

                --est;

                E e = p.item;

                current = p.next;

                action.accept(e);

                if (list.modCount != expectedModCount)

                    throw new ConcurrentModificationException();

                return true;

            }

            return false;

        }

 

        public int characteristics() {

            return Spliterator.ORDERED | Spliterator.SIZED | Spliterator.SUBSIZED;

        }

    }

 

ArrayListLinkedList的区别

ArrayList

实现了List接口

数据元素在内部用数组保存

指定索引的get/set速度很快

插入和删除数据元素在某些场合要花点时间

 

LinkedList

除了List接口外,还实现了双端队列,是双向链表

虽然指定索引的get/set不太灵光,但插入和删除数据元素要比ArrayList表现的要好些。


转载于:https://my.oschina.net/dubenju/blog/416509

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值