内部类
ArrayList有四个内部类,如下所示:
private class Itr implements Iterator<E>
private class ListItr extends Itr implements ListIterator<E>
private class SubList extends AbstractList<E> implements RandomAccess
static final class ArrayListSpliterator<E> implements Spliterator<E>
本文主要分析ArrayList的迭代器内部类的方法。
其中Itr类实现了Iterator接口,重写了hasNext()方法、next()方法和remove()方法等方法。
ListItr类继承了Itr类,且实现了ListIterator接口,重写了 hasPrevious()方法, nextIndex()方法,previousIndex()方法, previous()方法,set()方法和add()方法。
内部类Itr
关于内部类Itr,该类实现了Iterator接口,而且是一个私有内部类。我们可以通过ArrayList的iterator()方法获取。
成员变量
// cursor指向下一个元素
int cursor; // index of next element to return
// 返回最后一个元素的索引,假如没有了返回-1。其实他表示的就是当前元素的位置
int lastRet = -1; // index of last element returned; -1 if no such
// 存储集合修改次数的变量,为了判断是否有多个线程访问修改
int expectedModCount = modCount;
构造方法
默认空参构造。
成员方法
① hasNext方法
/**
* 判断是否还有下一个元素
* 原理是判断下一个元素的索引是否等于集合的大小
*/
public boolean hasNext() {
return cursor != size;
}
注意:hasNext方法不会导致游标cursor移动。
② next方法
/**
* 返回当前迭代的元素
*/
public E next() {
// 检查当前Itr修改次数和ArrayList是否一致,不一致则抛出并发修改异常
checkForComodification();
int i = cursor;
// 当cursor指向的索引大于集合的大小,就抛出没有元素异常,表示没有元素可以迭代
if (i >= size)
throw new NoSuchElementException();
// 把ArrayList.this.elementData的数据赋给局部对象数组elementData
Object[] elementData = ArrayList.this.elementData;
// 当cursor指向的索引大于集合缓存数组的长度时,抛出并发修改异常
// 原因是修改了集合。
if (i >= elementData.length)
throw new ConcurrentModificationException();
// cursor指向的位置后移
cursor = i + 1;
// lastRet加一,然后返回当前元素的值
return (E) elementData[lastRet = i];
}
关于第一个判断i >= size
i等于cursor,也就是需要返回的元素的下标,因为索引是从0开始,所以i,也就是cursor是不可能大于等于size的。
关于第二个判断i >= elementData.length
这个判断原因是集合数组被修改过,因为在经过第一个判断之后,i是一定小于size的,按道理是i<size<=element.length
的,所以i是不可能大于等于elementData.length的,除非你将集合数组的大小改变了,例如调用了trimToSize()方法。
③ remove方法
/**
* 移除当前元素
*/
public void remove() {
// 当lastRet小于0,抛出异常
if (lastRet < 0)
throw new IllegalStateException();
// 检查当前Itr修改次数和ArrayList是否一致,不一致则抛出并发修改异常
checkForComodification();
try {
// 调用ArrayList的remove方法移除当前索引的元素
ArrayList.this.remove(lastRet);
// 将当前索引赋值给cursor变量
cursor = lastRet;
// 使lastRet指针重置为-1
lastRet = -1;
// 调用ArrayList的remove方法会让modCount加一
// 重将新的修改次数赋值给expectedModCount变量
expectedModCount = modCount;
} catch (IndexOutOfBoundsException ex) {
throw new ConcurrentModificationException();
}
}
关于lastRet = -1;
这句,因为 lastRet 代表的那个元素已经被删除了,直接赋值为-1。
假如我们在单线程使用增强for循环遍历的时候使用ArrayList的remove方法删除元素,会抛出并发修改异常,假如我们使用迭代器的remove方法是不会出现这个问题的,因为在该迭代器的remove方法中更新了expectedModCount的值。expectedModCount = modCount;
注意:Itr的remove方法必须在next方法之后使用,且在next方法后只能使用一次。因为在一次remove时将lastRet已经置为-1了,再次进入remove时会判断 lastRet是否小于0。
内部类ListItr
该私有内部类,继承上面的私有内部类Itr,实现了ListIterator接口
private class ListItr extends Itr implements ListIterator<E>
成员变量
继承父类Itr的,如下:
// cursor指向下一个元素
int cursor; // index of next element to return
// 返回最后一个元素的索引,假如没有了返回-1。其实他表示的就是当前元素
int lastRet = -1; // index of last element returned; -1 if no such
// 存储集合修改次数的变量,为了判断是否有多个线程访问修改
int expectedModCount = modCount;
构造方法
/**
* 参数一般为数组的长度
*/
ListItr(int index) {
super();
cursor = index;
}
子类自己的成员方法
相对于Itr迭代器,这个迭代器除了有remove方法之外,还有add、set等方法。在一些方法中在调用ArrayList方法操作之后,都会重新设置expectedModCount的值,就不会在单线程的迭代过程中发生并发修改异常了。
① hasPrevious方法
/**
* 如果以逆向遍历列表,列表迭代器有多个元素,则返回 true。
*/
public boolean hasPrevious() {
return cursor != 0;
}
② nextIndex方法
/**
* 对next的后续调用所返回元素的索引
* 如果列表迭代器在列表的结尾,则返回列表大小。
*/
public int nextIndex() {
return cursor;
}
③ previousIndex方法
/**
* 对previous的后续调用所返回元素的索引
* 如果列表迭代器在列表的开始,则返回 -1。
*/
public int previousIndex() {
return cursor - 1;
}
④ previous方法(反向遍历)
/**
* 返回列表中的前一个元素。反向遍历
* 可以重复调用此方法来迭代列表,或混合调用 next 来前后移动
* (注意交替调用 next 和 previous 将重复返回相同的元素)
*/
public E previous() {
// 检查当前Itr修改次数和ArrayList是否一致,不一致则抛出并发修改异常
checkForComodification();
// 定义i为上个元素的下标
int i = cursor - 1;
// i小于0抛出异常
if (i < 0)
throw new NoSuchElementException();
// 将集合的缓冲数组赋值给变量elementData
Object[] elementData = ArrayList.this.elementData;
// 假如i大于缓冲数组的长度则抛出并发修改异常,原因是集合数组大小可能被修改了
if (i >= elementData.length)
throw new ConcurrentModificationException();
// cursor变量减一
cursor = i;
// 将i赋值给lastRet,相当于lastRet为当前元素的索引,并返回上个元素的值
return (E) elementData[lastRet = i];
}
⑤ set方法
/**
* 为当前下标重新赋值
*/
public void set(E e) {
// 当astRet小于0,抛出异常
if (lastRet < 0)
throw new IllegalStateException();
// 检查当前Itr修改次数和ArrayList是否一致,不一致则抛出并发修改异常
checkForComodification();
try {
// 调用ArrayList集合的set方法,将元素设置到lastRet索引的位置
ArrayList.this.set(lastRet, e);
} catch (IndexOutOfBoundsException ex) {
throw new ConcurrentModificationException();
}
}
⑥ add方法
public void add(E e) {
// 检查当前Itr修改次数和ArrayList是否一致,不一致则抛出并发修改异常
checkForComodification();
try {
int i = cursor;
// 调用ArrayList集合的add方法,将元素设置到下一个索引的位置
// 操作会使modCount的值加一
ArrayList.this.add(i, e);
cursor = i + 1;
// 重置lastRet
lastRet = -1;
expectedModCount = modCount;
} catch (IndexOutOfBoundsException ex) {
throw new ConcurrentModificationException();
}
}
原文始发于微信公众号(肝帝笔记):ArrayList-1-迭代器内部类
版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 举报,一经查实,本站将立刻删除。
文章由极客之音整理,本文链接:https://www.bmabk.com/index.php/post/31532.html