上次,测试了java集合类支持遍历方式的效率比较,今天再通过断电调试,去ArrayList底层的迭代器做了什么。
首先在迭代器这里打上断电,(在实际中变量ArrayList最后别用迭代器,因为他很慢)
可以看到这个iterator()方法返回值是一个迭代器,函数体是return new Itr()
点击下一步。可以看到Itr是一个ArrayList里面的一个类,从右边类结构图可以看到。ArrayList里面有四个类。
ArrayList的父类abstractList里面也有Itr这个类。
这里先点开Itr这个类,里面有哪些方法。
再次点击下一步。类成员初始化。
再次点击下一步。回到return语句。
再次点击下一步,返回到我们自己写的代码里面,开始执行hasnext()方法。
这里再点击下一步,进入hasnext()方法。这个方法是判断是否还有下一个元素
这里return语句 cursor是下一个元素返回的索引,size是容器的大小。
这里肯定不等于。返回true。
点击下一步,开始执行next()方法。
再次点击下一步,进入next()方法,这个方法是Itr内部的一个方法。这里先会去执行checkForComodficaation()这个方法,
这个方法主要是检查在迭代时,要迭代的容器的数量是否发生变化,如果发生变化,就抛异常
点击下一步,这里不会抛异常
再次点击下一步,把下一个元素索引0,赋值给当前元素的索引i,此时i为0.
点击下一步,此时判断索引是否大于等于了容器的大小,这里肯定没有。
再次点击下一步。这里会创建一个数组引用当前数组,然后再次判断下标 i 是否大于等于数组长度。
为什么要这样呢?因为这个新的element数组,是引用的之前我们的10万大小的数组,如果在迭代过程中增加了元素,下标 i 会增加,因为数据增加了,数据在数组里面对应是有下标的,也要多,那么肯定会比之前没增加数据的数据最大长度大或者相等。
如果出现这种情况,说明在迭代过程中,我们向容器里面添加元素,所以抛出异常。
再次点击下一步。到这一步,马上我们就能return要遍历的第一个元素了,所以为下一次遍历做准备
cursor+1
再次点击下一步,我们这里返回第一个元素,借助lastRet这个变量,这个变量初始值是最后一个变量索引,值为-1,
我们把当前要遍历的索引赋给它,然后返回这个数组即 elementData[0] 这样就完成了第一个元素的访问。
好了,ArrayList的迭代器方法和内部一个Itr类源码跟踪就结束了,下面再来看看get()这个方法,同样 打上一个可爱的断点。
点击下一步。get()方法是返回指定位置上的元素,从0开始,这里可以清晰看到get()方法,比 之前的next()方法简单多了,内部没那么多东西。
再次点击下一步,执行范围检查函数。如果索引大于等于了容量的实际存储的数据数量
比如一个长度为10的数组,里面有10个数 分别是1到10,此时的size是10,那么索引位9是最大的,index=9当然可以
但是index>=10,是绝对不可以的。或者数组里面只有1到5,此时size=5,数组最大容量为10,你获取index=4当然可以,但是获取index=5,此时这个位置是没有数的,所以会抛异常。
再次点击下一步,卧槽,此时不是应该返回一个element[index]么,这是什么!为什么还传个索引过去?
当我们再次点击下一步,会发现这是一个方法。此时的E是student,
这个方法其实就是返回一个强转的student类型的element[index]数组。
这里可以看出这个操作的封装力度很强,连强转都要封装。
好了,到此为止,就告一段落了。
后续楼主会继续学习集合类的源码,另外博客什么地方写错,请指出,大家一起学习进步!!