java集合类源码详解-ArrayList(8)-基于JDK8

这里是一些比较简单的ArrayList源码里面的函数

remove(int ):移除指定位置的数

fastremove(int):同样是移除指定位置的数,但是与remove(int)相比跳过检查,不返回被删除的数

补充下上次ArrayList的迭代遍历这种遍历方式在遍历的时候不能让其他线程或者本身的ArrayList来改变(通过add()或者remove方法等)容器,在迭代过程中要增加或者删除只能通过迭代器对象来执行。

比如下面的代码,肯定会报错

在第一次执行,还没有到add(3)这个方法时,modcount等于0.然而在一开始在调用ArrayList的iterator()方法时,在其Itr类的内部会执行这句

int expectedModCount = modCount;

专门用来记录迭代器迭代的集合对象的modcount,为了保证在迭代过程中,除了迭代器添加删除的其他操作老更改容器

所以在执行到add(3)时,ArrayList内部会modcount++,此时modcount会变了,而当再次执行到next()方法时。

next()方法内部的

checkForComodification
modcount和最开始expectedModCount不相等,会抛异常,说明在迭代过程中,有除了迭代器本身的其他操作改变了容器的相关属性。

另外一些简单的方法就不再赘述了,比如indexOf,lastIndexOf,outOfBoundsMsg等

接下来,来学习最后两个方法 toString(),hashCode() 来自于abstractCollection

abstractCollection重写了object的 tostring()方法。

equals()来自于abstractList

abstractCollection 是abstractList的父类

下面先测试tostring()

ArrayList的toString()继承自父类abstractList,abstractList继承自abstractCollection。

下面的abstractCollection里面toString的实现体。

第一行是这里abstractCollection类里面的 抽象方法 

然后abstractList 重写了这个抽象类

最后ArrayList又重写了这个方法

所以,这里我们猜想,在调用toString()方法的时候们肯定会去调用iterator() 方法,因为这里重写toString 其实就是想把集合里面的输出,而把数据里面的东西数组 自然就想到迭代器了。

这里我们点击dbug 开始调试。

点击下一步,进入了toString()方法

点击下一步,猜想正确,确实进入了iterator()方法。

点击下一步,让其加载完这个Itr类的属性为止

 点击下一步,回到了iterator()方法。

 点击下一步,这里会有一个逻辑判断 如果没有元素,就返回一个 [ ],这里我们有元素,所以,逻辑判断是false

再次点击下一步。

这里创建一个字符串 然后左边添加  [  , 这些都是为了调整输出格式

再次点击下一步,开始遍历集合中的值

点击下一步,这里判断元素e是否是当前对象,我们这里不是,这里会把e添加到sb末尾,然后下面再执行一个判断,如果没有到最后就再添加一个 ,和空格

到最后元素遍历完 就会在末尾加上 ] 

于是最终的输出结果显然就是这样

下面再看看equal()这个方法,这个方法会先判断如果比较的是自身 就会返回true,如果比较的不是List的特定类或者其子类实例就返回false。如果比较的对象 不是自身 但是是List的特定类或者及其子类,并在比较的这里两个集合对象拥有的内容的是一样的,也会返回true。

 所以,我们这里只测试两个对象 两个ArrayList的实例具有相同内容的运行情况。

先看看具有相同内容,和不同内容的结果

具有相同内容:

具有不同内容:

我们只看相同内容的情况,根据相同内容的情况,打上断点,点击下一步。在equals()方法内部会分布获取两个集合对象的迭代器

再点击下一步,这里会判断两个集合类对象是否还有下一个元素

 这里的逻辑判断肯定是true,点击下一步

分别迭代遍历两个集合的元素,并且把遍历出来的赋给o1和o2,这里有个三目运算,如果o1==null是false,那么就看02==null是否是false或者true ,如果o1==null是true,就比较o1和o2 ,通过equals方法。这里if判断肯定是false,所以不会执行if循环体,

所以,我们这里可以大胆推断,在01,02  都不为null时,是不会返回false的,如果这两个集合里面的数量不一样,最后一个return语句,肯定会返回false。

所以,我们这里可以得出结论,这个重写的equals()方法,当时比较自身返回true,比较的都是List的特定类或者是其子类实例  并且这个两个集合里面的数据数量一样,值一样,返回true。其他情况都是返回false。

接下来,debug hashCode(),当没有向两个集合里面添加任何元素的时候,输出的hash码为1

注释掉一些不必要的

 打上断点,点击下一步,这里会定义一个变量,并且把hashCode初始化为1

点击下一步,遇到一个增强型for循环,其实这里底层执行的是迭代器,点击下一步会进入到迭代器,再点击下一步会到Itr这个类内部,初始化类成员变量

 再点击下一步,回到增强型for循环

再点击下一步,跳转到hasNext()方法,判断这个集合里面是否有下一个值,

点击下一步,回到增强型for循环, (这里猜想只要hasNext()返回true,就会调用next开始遍历)再点击下一步,又跳转到next()开始遍历

点击下一步,最终增强型for循环,执行结束,把next()遍历的元素,返回给局部变量 e

再次点击下一步,为了方便追踪变量和内部实现,这里在源码里面的增强for循环的循环体里面加上两句话

 点击下一步,这里开始计算hashCode。注意 这里e.hashCode() 这个方法是计算 对应类型的hashCode

什么意思呢。我们这里指定的泛型是Integer,这里的 变量e 就是Integer类型,所以这里的e.hashCode()调用的是

Integer这个类里面的hashCode方法,而一个Integer类里面的hashCode方法,返回的就是自身的值的大小

ArrayList的hashCode()计算过程就是这样了(31*上个计算得到的hashCode,加上当前遍历的元素的对应类型的hashCode,然后不断这样迭代,知道遍历完集合里面的元素为止)。

到这里为止,ArrayList的源码就学习结束了!

猜你喜欢

转载自blog.csdn.net/qq_37889257/article/details/84541505