首页 > 编程语言 >深度理解Iterator底层源码

深度理解Iterator底层源码

时间:2023-07-01 10:31:26浏览次数:41  
标签:index 元素 Iterator ArrayList elementData modCount 源码 size 底层

public abstract class AbstractList<E> extends AbstractCollection<E> implements List<E> {
    //外部操作数:记录添加数据、删除数据的次数(记录元素个数变化的次数)
 	protected transient int modCount = 0;//4
}

这段代码是一个抽象类AbstractList,实现了List接口。下面是对代码的解析:

  1. AbstractList类的定义:
  • E:泛型参数,表示列表中的元素类型。
  • AbstractCollection:继承了AbstractCollection抽象类,实现了Collection接口。
  • List:实现了List接口。
  1. modCount变量:
  • modCount:记录添加数据、删除数据的次数,即记录元素个数变化的次数。
  • transient修饰符:表示该变量不会被默认的序列化机制序列化,即不会被保存到文件中。
  • 初始值为0。
  1. modCount变量的作用:
  • 在集合框架中,modCount变量用于实现"快速失败"机制。
  • 当对集合进行迭代操作时,如果在迭代过程中集合的结构发生了变化(例如添加或删除了元素),那么就会抛出ConcurrentModificationException异常,以避免出现并发修改的问题。
  • modCount变量记录了集合结构发生变化的次数,每次添加或删除元素时,都会增加modCount的值。
  • 在迭代操作中,会将modCount的值保存下来,然后在迭代的过程中检查modCount是否发生了变化,如果发生了变化,则抛出异常。
  • 这样可以在迭代过程中及时发现并发修改的情况,避免出现不一致的问题。
public class ArrayList<E> extends AbstractList<E> implements List<E>{
    //数据容器
    transient Object[] elementData;//["aaa","bbb","ccc","ddd",null,null,...]
    //元素个数/指针
    private int size;//4
    
    public boolean add(E e) {
        ensureCapacityInternal(size + 1);  // Increments modCount!!
        elementData[size++] = e;
        return true;
    }
    
    private void ensureCapacityInternal(int minCapacity) {
        if (elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA) {
            minCapacity = Math.max(DEFAULT_CAPACITY, minCapacity);
        }
        ensureExplicitCapacity(minCapacity);
    }
    private void ensureExplicitCapacity(int minCapacity) {
        modCount++;

        // overflow-conscious code
        if (minCapacity - elementData.length > 0)
            grow(minCapacity);
    }
    
    public boolean remove(Object o) {
        if (o == null) {
            for (int index = 0; index < size; index++)
                if (elementData[index] == null) {
                    fastRemove(index);
                    return true;
                }
        } else {
            for (int index = 0; index < size; index++)
                if (o.equals(elementData[index])) {
                    fastRemove(index);
                    return true;
                }
        }
        return false;
    }
    
    private void fastRemove(int index) {
        modCount++;
        int numMoved = size - index - 1;
        if (numMoved > 0)
            System.arraycopy(elementData, index+1, elementData, index,
                             numMoved);
        elementData[--size] = null; // clear to let GC do its work
    }
    
    public Iterator<E> iterator() {
        return new Itr();
    }
    
    private class Itr implements Iterator<E> {
        int cursor;       // 游标 - 4
        int lastRet = -1; // 遍历当前元素的下标 - 3
        int expectedModCount = modCount;//内部操作数 - 4

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

        @SuppressWarnings("unchecked")
        public E next() {
            checkForComodification();//判断内外操作数是否一致,不一致就报错
            //i = 3
            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的remove方法去做删除
                ArrayList.this.remove(lastRet);
                //把当前的元素下标赋值给游标
                cursor = lastRet;
                lastRet = -1;
                //重新将外部操作数赋值给内部操作数,保证了内外部操作数是一致的
                expectedModCount = modCount;
            } catch (IndexOutOfBoundsException ex) {
                throw new ConcurrentModificationException();
            }
        }

        final void checkForComodification() {
            //外部操作数不等于内部操作数就报错
            if (modCount != expectedModCount)
                throw new ConcurrentModificationException();
        }
    }
}

这段代码是一个ArrayList类,继承了AbstractList类,实现了List接口。下面是对代码的解析:

  1. ArrayList类的定义:
  • E:泛型参数,表示列表中的元素类型。
  • AbstractList:继承了AbstractList抽象类,实现了List接口。
  • List:实现了List接口。

elementData数组:

  • elementData:数据容器,用于存储ArrayList中的元素。
  • transient修饰符:表示该变量不会被默认的序列化机制序列化,即不会被保存到文件中。
  • Object[]:数组类型为Object,可以存储任意类型的对象。
  • 初始值为null。

size变量:

  • size:元素个数/指针,表示ArrayList中当前存储的元素个数。
  • 初始值为0。
  1. add方法:用于向ArrayList中添加元素。
  1. 参数e:要添加的元素。
  2. ensureCapacityInternal方法:确保ArrayList的容量足够存放新的元素。
  • size + 1:计算新的元素个数。
  • 调用ensureCapacityInternal方法,传入新的元素个数,确保容量足够。
  1. elementData[size++] = e:将新的元素添加到elementData数组中,并将size加1。
  • elementData:数据容器,存储ArrayList中的元素。
  • size++:先将size的值赋给elementData数组的下标,然后将size加1。
  • e:要添加的元素。
  1. 返回true表示添加成功
  1. ensureCapacityInternal方法,用于确保ArrayList的容量足够存放新的元素。
  1. 参数minCapacity:最小需要的容量。
  2. 判断elementData数组是否为默认空数组DEFAULTCAPACITY_EMPTY_ELEMENTDATA。
  • DEFAULTCAPACITY_EMPTY_ELEMENTDATA:默认的空数组,用于表示ArrayList初始时没有元素。
  • 如果elementData数组是默认空数组,表示ArrayList是刚刚创建的,容量为0。
  • 此时,需要根据minCapacity和DEFAULT_CAPACITY的比较结果来确定新的容量。
  1. Math.max(DEFAULT_CAPACITY, minCapacity):取DEFAULT_CAPACITY和minCapacity中的较大值。
  • DEFAULT_CAPACITY:默认的初始容量,为10。
  • minCapacity:最小需要的容量。
  • 如果minCapacity小于DEFAULT_CAPACITY,将新的容量设为DEFAULT_CAPACITY。
  1. 调用ensureExplicitCapacity方法,传入新的容量,确保容量足够。
  • ensureExplicitCapacity方法会根据新的容量和当前容量的大小关系,决定是否需要扩容。
  1. ensureExplicitCapacity方法,用于确保ArrayList的容量足够存放新的元素。
  1. 参数minCapacity:最小需要的容量。
  2. modCount++:增加modCount的值。
  • modCount:用于记录ArrayList结构发生变化的次数,用于迭代器的快速失败机制。
  1. 判断minCapacity与elementData数组的长度的差值是否大于0。
  • 如果minCapacity大于elementData数组的长度,表示当前容量不足以容纳新的元素。
  • 需要调用grow方法进行扩容。
  1. 调用grow方法,传入minCapacity,进行扩容操作。
  • grow方法会根据新的容量需求,进行扩容操作。
  1. remove方法,用于从ArrayList中移除指定的元素。
  1. 参数o:要移除的元素。
  2. 判断参数o是否为null。
  • 如果o为null,表示要移除的元素为null。
  • 遍历elementData数组,查找第一个为null的元素。
  • 调用fastRemove方法,传入找到的元素的索引,进行快速移除。
  • 返回true表示成功移除了元素。
  1. 如果o不为null,表示要移除的元素不为null。
  • 遍历elementData数组,查找第一个与o相等的元素。
  • 调用fastRemove方法,传入找到的元素的索引,进行快速移除。
  • 返回true表示成功移除了元素。
  1. 如果遍历完整个elementData数组都没有找到要移除的元素,则返回false表示没有移除任何元素。
  1. fastRemove方法,用于快速移除指定索引位置的元素。
  1. 参数index:要移除的元素的索引。
  2. modCount++:增加modCount的值。
  3. 计算需要移动的元素个数。
  • numMoved = size - index - 1:计算需要移动的元素个数,即从index位置开始,到最后一个元素之间的元素个数。
  1. 如果需要移动的元素个数大于0,表示需要进行元素的移动操作。
  • 使用System.arraycopy方法,将从index+1开始的元素复制到index位置,覆盖掉要移除的元素。
  • 这样就实现了快速移除。
  1. 将最后一个元素置为null。
  • elementData[--size] = null:将最后一个元素置为null,以便垃圾回收机制回收。
  • 这样就完成了元素的快速移除操作。
  1. iterator方法,用于返回一个迭代器对象。
  1. 返回类型:Iterator,表示返回的是一个实现了Iterator接口的对象,可以用于遍历ArrayList中的元素。
  2. 创建一个新的Itr对象并返回。
  • Itr是ArrayList的内部类,实现了Iterator接口。
  • 通过返回Itr对象,可以使用该对象的方法来遍历ArrayList中的元素。
  1. ArrayList类中的Itr内部类,实现了Iterator接口,用于遍历ArrayList中的元素。
  1. 属性:
  • cursor:游标,表示当前遍历的元素的索引。
  • lastRet:上一个遍历的元素的索引。
  • expectedModCount:内部操作数,用于判断内外部操作数是否一致。
  1. hasNext方法:
  • 判断游标是否等于size,如果等于size,表示已经遍历到了最后一个元素,返回false;否则返回true。
  1. next方法:
  • 调用checkForComodification方法,判断内外部操作数是否一致,如果不一致,抛出ConcurrentModificationException异常。
  • 将游标的值赋给局部变量i。
  • 如果i大于等于size,表示已经遍历到了最后一个元素,抛出NoSuchElementException异常。
  • 获取外部ArrayList对象的elementData数组。
  • 如果i大于等于elementData数组的长度,表示在遍历过程中,ArrayList的结构发生了改变,抛出ConcurrentModificationException异常。
  • 将游标的值加1,表示遍历到下一个元素。
  • 返回elementData数组中lastRet索引位置的元素,并将i赋给lastRet。
  1. remove方法:
  • 如果lastRet小于0,表示没有执行过next方法或者已经执行过remove方法,抛出IllegalStateException异常。
  • 调用checkForComodification方法,判断内外部操作数是否一致,如果不一致,抛出ConcurrentModificationException异常。
  • 使用ArrayList的remove方法,根据lastRet的值删除元素。
  • 将游标的值赋给lastRet。
  • 将lastRet的值置为-1,表示已经执行过remove方法。
  • 将外部操作数的值赋给内部操作数,保证内外部操作数一致。
  • 如果捕获到IndexOutOfBoundsException异常,表示在遍历过程中,ArrayList的结构发生了改变,抛出ConcurrentModificationException异常。
  1. checkForComodification方法:
  • 判断内外部操作数是否一致,如果不一致,抛出ConcurrentModificationException异常。
场景:
    ArrayList<String> list = new ArrayList<>();
    list.add("aaa");
    list.add("bbb");
    list.add("ccc");
    list.add("ddd");

    Iterator<String> it = list.iterator();
    while(it.hasNext()){
        String element = it.next();
        System.out.println(element);
    }

使用了ArrayList的iterator方法返回一个迭代器对象,然后通过迭代器遍历ArrayList中的元素,并打印出来。

  1. 创建一个ArrayList对象,并添加了四个元素。
  2. 调用ArrayList的iterator方法返回一个迭代器对象,赋值给it。
  3. 使用while循环,判断迭代器是否还有下一个元素,如果有,则执行循环体内的代码。
  4. 调用迭代器的next方法获取下一个元素,并将其赋值给element。
  5. 打印element。
  6. 循环结束后,所有元素都被遍历完毕,程序结束。

标签:index,元素,Iterator,ArrayList,elementData,modCount,源码,size,底层
From: https://blog.51cto.com/u_16154651/6598328

相关文章

  • 离线安装ffmpeg源码包【详细教程】
    今天分享一下ffmpeg源码包的安装过程,针对在没有网络环境下,且不能直接使用yum如何成功安装ffmpeg源码包。博主本人通过正式服务器测试,记录整个安装过程。值得大家收藏同时,我会分享一下如何使用ffmpeg对H.264格式视频(MP4)进行m3u8+ts切片的转换,并生成m3u8+ts格式文件ffmpeg所需要环......
  • Debug Golang源码中的单元测试
    goland配置如上,既可以debuggolang源码中的单元测试。......
  • 【源码分析】Mybatis 的配置解析过程
    博主介绍:✌博主从事应用安全和大数据领域,有8年研发经验,5年面试官经验,Java技术专家✌Java知识图谱点击链接:体系化学习Java(Java面试专题)......
  • 深入学习 Mybatis 的四大组件源码
    博主介绍:✌博主从事应用安全和大数据领域,有8年研发经验,5年面试官经验,Java技术专家✌Java知识图谱点击链接:体系化学习Java(Java面试专题)......
  • UE5 源码下载编译过程记录
    前言没有科学上网,就不要折腾了1注册Epic2注册github3关联账号在UE官网登入账号并且关联github账号4下载源码5执行Setup.bat5.1执行出错提示FailedtodownloadFailedtodownload'http://cdn.unrealengine.com/dependencies/UnrealEngine-24819931/19acf26186763763ae43ec3e4bd1......
  • 贪吃蛇游戏制作(附源码)
    CSS:部分*{margin:0;padding:0;}.wrap{width:600px;margin:0auto;position:relative;}p{position:absolute;left:73%;top:10%;}h1{text-align:center;margin-bottom:20px;}#score{text-a......
  • 直播网站源码,背景色渐变
    直播网站源码,背景色渐变实现页面从白色背景过度到蓝色 vart=d3.transition()  .duration(2000);d3.select("body").transition(t).style("background-color","lightblue");constcolors=['red','yellow','blue']letj=0fu......
  • 校园APP小程序H5,免费源码,允许二开。
    点击查看免费完整源码,允许二开50% { transform: scale(0.3); -webkit-transform: scale(0.3); opacity: 0.3; } 75% { transform: scale(0.5); -webkit-transform: scale(0.5); opacity: 0.5; } 100% { transform: scale(0.8); -webkit-transform: scale(0.8); ......
  • 一个基于STM32H743芯片和SOEM协议栈的EtherCAT主站源码。该源码提供了配套的CUBE工程,
    一个基于STM32H743芯片和SOEM协议栈的EtherCAT主站源码。该源码提供了配套的CUBE工程,使用的是SOEM协议栈的1.3.1版本。此外,还可以使用NUCLEO-H743ZI开发板进行配套开发。该系统支持DC同步,并且可以与多种驱动器型号配合使用,包括汇川IS620N、三洋RS3、赛孚德ASD620B、埃斯顿ProNet、......
  • 关于30KW储能PCS逆变器的设计方案。它包括双向DCDC和三电平逆变PCS。资料中提供了仿真
    关于30KW储能PCS逆变器的设计方案。它包括双向DCDC和三电平逆变PCS。资料中提供了仿真源码,其中包含并网和离网两个模型30KW储能PCS逆变器双向变流器设计方案资料1.此系列为30KW储能PCS逆变器设计方案资料,双向DCDC和三电平逆变PCS;2.仿真源码含有并网和离网两个模型;3.原理图(PDF)含......