Java之LinkedList源码分析(第九篇:迭代器ListItr功能分析)

    (备注:本文基于JDK1.8)

    LinkedList重写了listIterator()方法,父类AbstractList中定义了抽象方法listIterator()、AbstractList的父类AbstractSequentialList也定义了抽象方法listIterator()。listIterator()抽象方法是定义在List接口中的一个规范,它表示获得一个列表迭代器,LinkedList具体的实现了此listIterator()方法,它会返回具备双向遍历元素能力的迭代器对象,今天我们一起学习一下LinkedList中的双向迭代器!

 

ListItr类结构

    private class ListItr implements ListIterator<E> {
   ………………省略很多源码………………
}

ListItr类定义在LinkedList类的内部(作为普通内部类),它实现了ListIterator接口。因为ListItr是普通内部类,它产生的每一个对象会隐式的持有每个外部类对象的引用……

 

ListItr类重要字段介绍

        private Node<E> lastReturned;
        private Node<E> next;
        private int nextIndex;
        private int expectedModCount = modCount;

lastReturned 表示ListItr对象持有的,最后一次访问的元素对象

next 表示ListItr对象下一次即将访问的元素(假设迭代器第一次创建,并且我们设想最左到最右,此时的next的初始值为指向第一个元素,每当你调用它访问元素的方法,它返回的是就是next指向的元素)

nextIndex 表示ListItr对象下一次要访问的元素的下标

expectedModCount 表示ListItr持有的预期的修改次数,该字段专门用于判断是否有多个线程同时修改LinkedList持有的元素,看见那个modCount了吗?modCount是LinkedList对象持有的修改次数,此处能调用的原因是ListItr对象隐式持有外部类对象的引用!fail-fast机制必备!

 

ListItr功能介绍

1、改变元素数量功能

add():LinkedList对象中添加一个元素

remove():LinkedList对象中删除一个元素

2、向前遍历元素功能

hasPrevious():判断是否有上一个元素可访问

previous():获得上一个访问的元素

previousIndex():获得上一个访问的元素在线性表中的位置(第一个元素的位置是0)

3、向后遍历元素功能

hasNext():判断是否有下一个元素可访问

next():获得下一个访问的元素

nextIndex():获得下一个访问的元素在线性表中的位置(第一个元素的位置是0)

4、容错功能

checkForComodification() :防止多线程下修改LinkedList的元素

5、遍历元素功能

forEatchRemaining():遍历剩余元素(没有访问过的元素,就是剩余的元素)

 

PS:接下来逐个分析这些方法……

 

add()方法分析

        public void add(E e) {
            checkForComodification();
            lastReturned = null;
            if (next == null)
                linkLast(e);
            else
                linkBefore(e, next);
            nextIndex++;
            expectedModCount++;
        }

用于向LinkedList中已遍历完的元素的前面添加一个元素

举个例子:当你遍历到第二个元素的时候,你调用此add(E)方法,此时新插入的元素将作为第二个元素,曾经的第二个元素将成为第三个元素!

1、检查是否发生并发修改,防止多线程下使用LinkedList

由checkforComodification()方法检查是否发生过多个线程下操作LinkedList的情况,只要发生过操作元素的行为,就会抛出一个ConcurrentModificationException异常对象

2、将迭代器最后一次访问元素的标记初始化为null

lastReturned指向最后一次访问的元素对象

3、针对空的LinkedList做处理

当ListItr对象持有的next为null,说明LinkedList并没有元素,因为ListItr对象创建时,持有的next会指向第一个元素,此时使用linkLast()方法完成元素的添加

4、对LinkedList非空的情况,将新增的元素添加到当前遍历元素的前面

通过调用linkBefore()方法完成添加工作,新增加的元素将成为当前即将遍历元素的前一个元素!

5、将下次访问元素的下标增加1

nextIndex增加1

6、fail-fast机制保护,防止多线程下使用LinkedList

将expectedModCount增加1

 

remove()方法分析

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

用于删除最后一次遍历过的元素

1、fail-fast机制保护

通过调用checkForComodification()方法

2、针对没有遍历过元素的情况……直接抛出异常警告用户

当lastReturned指向null,说明没有遍历过任何一个元素,此时抛出IllegalStateException对象告知用户错误行为

3、取出来当前遍历元素的下一个元素,由局部变量lastNext负责保存

4、调用unlink()方法删除最后遍历过的元素

5、什么情况最后一次遍历过的元素与即将遍历的元素会是同一个呢?

next == lastReturned?有点懵逼……

6、正常情况下,nextIndex减去1

7、更新最后遍历过的元素为空

lastReturned为null

8、fail-fast机制保护,将expectedModCount增加1

 

hasPrevious()方法分析

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

用于判断是否有上一个元素的方法

通过对ListItr对象持有的nextIndex值进行判断,当nextIndex值大于0时,则返回true,表示存在上一个元素

 

previous()方法分析

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

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

用于遍历上一个元素的方法

1、fail-fast检测,防止在多线程下使用ListItr遍历元素

调用checkForComodification()方法完成并发检测

2、检查是否有上一个元素

通过调用hasPrvious()方法检测是否有可以遍历的上一个元素,没有可遍历的元素时,会抛出NoSuchElementException对象

3、查找到本次需要遍历的节点对象,两种情况做出处理

第一种情况:已经遍历完所有元素

ListItr对象持有的lastReturned存储着最后一次返回的元素,如果next为null,说明已经遍历完最后一个元素,此时如果调用previous()方法,则是向前遍历元素,所以将LinkedList对象持有的实例变量last赋值给next、然后再将next值赋值给lastReturned,完成更新

另外一种情况:遍历到中间的元素

获取next指向节点对象持有的prev,prev表示上一个节点对象,赋值给next、以及next再赋值给lastReturned

4、更新元素的索引值

next减去1

5、返回节点对象持有的元素

返回lastReturned指向的节点对象持有的item

 

previousIndex()方法分析

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

用于获取即将向左侧遍历的元素的索引值

 

hasNext()方法分析

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

用于返回是否存在下一个可遍历元素的方法

nextIndex小于表示元素总数的size值,说明仍然有更多元素可以遍历,nextIndex与size值相等或者大于,就表示已经没有更多元素可以遍历了

 

next()方法分析

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

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

用于遍历下一个元素的方法,previous()方法是向左侧遍历、next()方法则是向右侧遍历

 

nextIndex()方法分析

        public int nextIndex() {
            return nextIndex;
        }

用于表示返回下一个即将遍历元素的索引值

 

checkForComodification()方法分析

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

用于fail-fast机制检查的方法

当LinkedList对象持有的modCount值(定义在父类AbstractList中)与ListItr对象持有的expectedModCount值不相等时,说发生过并发修改,抛出ConcurrentModificationException异常对象,告知用户不能在多线程环境下使用LinkedList

 

forEachRemaining()方法分析

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

用于遍历完所有剩余元素的方法,必须传入一个Consumer对象,Consumer对象中的accept()方法可以做什么工作呢?有机会用一次这个方法

 

总结

1、迭代器不仅遍历元素,还可以添加、删除元素

2、迭代器定义在LinkedList的内部,每个迭代器对象隐式持有LinkedList对象的引用

 

  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值