迭代器源码分析

迭代器源码分析

import java.util.Iterator;

public class Demo2 {
    public static void main(String[] args) {
        //迭代器源码分析

        //创建一个集合并且添加元素
        ArrayList<String> list=new ArrayList<>();
        list.add("aaa");
        list.add("bbb");
        list.add("ccc");

        /*
         * iterator()  生成一个迭代器对象,默认指向集合0索引处
         * hasNext()   判断当前指向的位置是否又元素
         * next()      1.获取元素  2.移动指针
         * */
        Iterator<String> it=list.iterator();
        while (it.hasNext()){
            String str=it.next();
            System.out.println(str);
        }
    }
}

我们知道这个Iterator iterator()是ArrayList包下的方法,我们要想创建他的对象可以打点调用

首先我们进入iterator()的源码

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

这里可以看到他是创建了一个Itr()的对象进行返回

private class Itr implements Iterator<E> {
    int cursor;       // index of next element to return
    int lastRet = -1; // index of last element returned; -1 if no such
    int expectedModCount = modCount;

    // prevent creating a synthetic constructor
    Itr() {}

    public boolean hasNext() {
        return cursor != size;
    }

    @SuppressWarnings("unchecked")
    public E next() {
        checkForComodification();
        int i = cursor;
        if (i >= size)
            throw new NoSuchElementException();
        Object[] elementData = ArrayList.this.elementData;
        if (i >= elementData.length)
            throw new ConcurrentModificationException();
        cursor = i + 1;
        return (E) elementData[lastRet = i];
    }

    public void remove() {
        if (lastRet < 0)
            throw new IllegalStateException();
        checkForComodification();

        try {
            ArrayList.this.remove(lastRet);
            cursor = lastRet;
            lastRet = -1;
            expectedModCount = modCount;
        } catch (IndexOutOfBoundsException ex) {
            throw new ConcurrentModificationException();
        }
    }

    @Override
    public void forEachRemaining(Consumer<? super E> action) {
        Objects.requireNonNull(action);
        final int size = ArrayList.this.size;
        int i = cursor;
        if (i < size) {
            final Object[] es = elementData;
            if (i >= es.length)
                throw new ConcurrentModificationException();
            for (; i < size && modCount == expectedModCount; i++)
                action.accept(elementAt(es, i));
            // update once at end to reduce heap write traffic
            cursor = i;
            lastRet = i - 1;
            checkForComodification();
        }
    }

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

我们拆开了阅读

private class Itr implements Iterator<E> {
    int cursor;       // index of next element to return
    int lastRet = -1; // index of last element returned; -1 if no such
    int expectedModCount = modCount;

这里可以看到,这里是一个内部类实现了Iterator接口

定义了三个成员变量

分别表示

第一个:有一个光标的意思,可以理解为指针

第二个:表示上一次索引位置,默认为:-1

第三个:跟并发修改异常有关,待会解释

Itr() {}

当我们使用空参构造创建迭代器的对象的时候,默认指针的索引是0,上一次也就是-1

接下来分析hasNext()方法

public boolean hasNext() {
    return cursor != size;
}

这里就是拿到指针0和集合大小进行判断看是否相等,不相等就返回true表示集合中有元素,相等就返回false,表示这个位置没有元素

明显我在集合中添加了三个元素,size=3

接下来看next()方法

public E next() {
    checkForComodification();
    int i = cursor;
    if (i >= size)
        throw new NoSuchElementException();
    Object[] elementData = ArrayList.this.elementData;
    if (i >= elementData.length)
        throw new ConcurrentModificationException();
    cursor = i + 1;
    return (E) elementData[lastRet = i];
}

第一句暂时忽略,待会讲

首先,定义了i等于cursor,可以知道初始是0,表示的是上一次操作索引的位置

接下来走到if语句,明显不满足,这一步其实就是说,如果上一次操作的索引等于了这个size,也就是超出原集合最大索引了,就会报一个异常NoSuchElementException,这个叫没有这个元素异常

走到Object[] elementData = ArrayList.this.elementData;

我们知道ArraysList底层是数组elemenData,也就是把集合底层数组的地址拿过来赋值给Object[] elementData,方便下次使用

然后继续判断这一步也暂时忽略

cursor = i + 1;
return (E) elementData[lastRet = i];

关键是这里,首先,把指针往后移动了一个

然后把移动之前也就是0索引位置的元素做了一个返回,最后我们使用str接收,并且做了一个打印

这里就体现出来了,移动指针,并且返回上一次索引中的数据

反复执行一直到 i 表示的是最后一个元素的时候,把指针移动到了数组最外面一个单位,并且将最后一个数据进行返回

如果继续强行移动指针,并且获取下一个元素

if (i >= size)
    throw new NoSuchElementException();

就会报NoSuchElementException()异常

最后我们将一下这个modCount,其实他相当于集合变化的次数

为什么怎么说你,还是扒源码

public boolean add(E e) {
    modCount++;
    add(e, elementData, size);
    return true;
}
public E remove(int index) {
    Objects.checkIndex(index, size);
    final Object[] es = elementData;

    @SuppressWarnings("unchecked") E oldValue = (E) es[index];
    fastRemove(es, index);

    return oldValue;
}
private void fastRemove(Object[] es, int i) {
    modCount++;
    final int newSize;
    if ((newSize = size - 1) > i)
        System.arraycopy(es, i + 1, es, i, newSize - i);
    es[size = newSize] = null;
}

我们在add和remove方法中都发现了modCount++,所以就可以证明他是修改次数的意思

这下我们回到next()看第一句执行代码

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

这里就可以看到

第一个modCount是集合变化的次数,和创建的时候的次数进行比较,如果不相等,就会发生,并发修改异常ConcurrentModificationException(),这也是为什么使用迭代器的时候,不能用集合的方式进行增删,这跟next()方法中的第二个判断也就是一个意思,如果使用了集合的方式进行增删,次数肯定不想等,从而引发并发修改异常

所以要使用迭代器自带的增删方式,最后迭代器源码就分析完毕!

posted @ 2022-11-09 22:30  喜欢七岁就很浪  阅读(78)  评论(0编辑  收藏  举报