JAVA集合框架源码之LinkedList(二)

本文详细解析了LinkedList的迭代器实现,包括其内部结构、遍历操作、截取操作等,重点介绍了ListIterator的实现细节。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

之前看完了LinkedList中的一些基本方法,这一篇再看看稍微复杂一点的方法。首先放一张LinkedList的继承关系图。

对于List来说,使用最多的操作估计就是遍历了,遍历操作一般都是使用循环。当然在1.8之后 Iterable 接口新增了一个 forEach 方法,由于LinkedList及其父类都没有实现这个方法,所以调用 LinkedList·的forEach方法就是默认的实现,使用增强FOR循环来遍历,然后对每一个元素执行一个action操作。

// 省略其他方法
public interface Iterable<T> {

    Iterator<T> iterator();

    default void forEach(Consumer<? super T> action) {
        Objects.requireNonNull(action);
        for (T t : this) {
            action.accept(t);
        }
    }

}

我们都知道,增强FOR循环本质上都是使用的迭代器来遍历,接着我们再来看看LinkedList的迭代器。获取迭代器有三个方法可以获取,分别是来自 Iterable 接口的 iterator 方法作为通用的迭代器;来自 List 接口的两个 listIterator 方法,用作针对List设计的迭代器,功能更多。

// 省略其他方法
public interface List<E> extends Collection<E> {

    ListIterator<E> listIterator();

    ListIterator<E> listIterator(int index);
}

尽管有三个方法可以获取到迭代器,但是LinkedList的迭代器却只有一个。

// 省略其他方法
public abstract class AbstractSequentialList<E> extends AbstractList<E> {

    public Iterator<E> iterator() {
        return listIterator();
    }

    public abstract ListIterator<E> listIterator(int index);
}


public abstract class AbstractList<E> extends AbstractCollection<E> implements List<E> {

    public ListIterator<E> listIterator() {
        return listIterator(0);
    }

}

在 LinkedList 的父类中可以看到,最终调用的都是 listIterator(index)方法。现在我们再去 LinkedList 中看看这个方法的实现。

    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();
            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;
        }

        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();
        }

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

显然,LinkedList 内部实现了一个私有的 ListIterator ,构造函数接收一个index作为参数,通过node(index)方法来找到迭代的起点。在来看看内部属性:

lastReturned:上一次返回的节点;

next:下一个要遍历的节点;

nextIndex:下一个要遍历的节点位置;

expectedModCount:初始化等于modCount,为了标识在遍历过程中除了当前迭代器外 LinkedList 没有其他的增删改操作。

再看方法实现:

hasNext:判断是否还有节点未遍历;

next:返回下一个遍历的节点。先判断链表是否被其他线程改过,再判断是否还有未遍历的节点。再将 lastReturned 指向 next,next指向next.next,nextIndex++,最后返回原next.item。

hasPrevious:判断当前节点是否有前一个节点;

previous:返回前一个节点;和next方法类似,区别在于next指向了next.prev,nextIndex--。而且,lastReturned和next指向的是同一个节点。

nextIndex:返回当前节点的下一个位置;

previousIndex:返回当前节点的前一个位置;

remove:从链表中删除上一个返回的节点;先判断链表是否被其他线程改过,再判断上一个返回的节点是否为null;先把lastReturned.next保存一份,再将lastReturned节点从链表中删除,由于previous方法使得lastReturned和next指向的是同一个节点,所以next == lastReturned 表示之前是从后向前迭代的,所以需要把next指向lastReturned.next;如果是从前向后迭代的,那么只需要将nextIndex--即可,即下一个要遍历的元素位置还是当前位置,最后维护下lastReturned和expectedModCount。

set:更新上一个返回的节点的值;

add:添加元素到链表中;如果已经到了尾部则添加到最后,否则添加到下一个要遍历的节点之前;

forEachRemaining:遍历剩余节点;对下一个要遍历的节点一直到尾部的节点,全部执行一次action操作。对每个节点的操作都需要判断modCount和expectedModCount;

遍历的操作看完之后还有截取的操作,主要是 subList(fromIndex,toIndex)方法。这个方法来自父类AbstarctList,LinkedList并未实现,在我的另外一篇 文章 中对这个方法已经读过一遍了,这里就不再看了。

到这里LinkedList的一些源码基本都读的差不多了,等有时间了再去研究一些难度较高的源码吧。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值