1.迭代器体系介绍:
AbstractList类中的迭代器为目标,分析迭代器主要的实现和继承:
- Iterator:作用在集合上的最底层接口
- ListIterator:作用在list上的迭代器,继承Iterator类。根据list的特点,新增了set、add、previous等方法。
- AbstractList内部定义了两个私有的迭代器:
3.1 Itr:实现Iterator接口,定义了Iterator接口方法的实现逻辑。
3.2 ListItr:继承Itr类、实现ListIterator接口,定义了ListIterator接口方法的实现逻辑。
ArrayList作为AbstractList的子类,也自定了两个内部迭代器,所以ArrayList调用的是自身的迭代器,但方法的实现逻辑是相同的。文章仅分析AbstractList的迭代器方法及实现,其他子类类同。
2. 迭代器调用方法
AbstractList主要有3个调用迭代器方法:
- iterator() :返回Iterator迭代器
- listIterator():返回listIterator迭代器
- listIterator(final int index) :返回特定位置后的集合元素的迭代器
#迭代器调用源码:
public abstract class AbstractList<E> extends AbstractCollection<E> implements List<E> {
public Iterator<E> iterator() {// 返回迭代器
return new Itr();
}
public ListIterator<E> listIterator() { // 返回迭代器
return listIterator(0);
}
public ListIterator<E> listIterator(final int index) {// 返回特定位置之后的迭代器
rangeCheckForAdd(index);
return new ListItr(index);
}
}
3.迭代器详解
3.1 Iterator接口
Iterator类是作用在集合上的最底层接口,仅仅定义了4个接口方法:hasNext()、next()、remove()、forEachRemaining
#Iterator接口源码
public interface Iterator<E> {
boolean hasNext();
E next();
default void remove() {
throw new UnsupportedOperationException("remove");
}
default void forEachRemaining(Consumer<? super E> action) {
Objects.requireNonNull(action);
while (hasNext())
action.accept(next());
}
}
3.2 ListIterator接口
ListIterator类是作用在list上的迭代器,继承Iterator。
依据list集合特点(索引位置可以确定集合元素),在Iterator基础上新增几类方法:
- next返回值的下标索引:nextIndex
- next返回值的上一个元素相关信息:hasPrevious、previous、previousIndex
- 迭代器中元素的新增和替换:add、set
#ListIterator源码
public interface ListIterator<E> extends Iterator<E> {
boolean hasNext();
E next();
boolean hasPrevious();
E previous();
int nextIndex();
int previousIndex();
void remove();
void set(E e);
void add(E e);
3.3 Itr和ListItr类
Itr和ListItr类是AbstractList中的两个内部私有类,分别实现了Iterator和ListIterator类中的方法。
3.1 Itr类
Itr实现Iterator接口,定义了两个指针和一个计数器,分别用于迭代循环和是线程同步的问题:
- cursor:数组指针
- lastRet:上一次调用next或previous时的元素下标
- expectedModCount:修改次数
Itr实现了hasNext、next、remove方法的处理逻辑:
- next:先返回指针元素值,然后指针下移
- hasNext:指针位置是否越界
- remove:调用list.remove方法,指针上移;lastRet设为-1,保证remove只能调用一次。
方法调用时,先调用checkForComodification方法,保证list的修改次数与迭代器的相同,所以在调用迭代器时不能对list进行修改操作。
方法处理逻辑结束后,进行expectedModCount==moCount,保证修改次数的一致性。
public abstract class AbstractList<E> extends AbstractCollection<E> implements List<E> {
protected transient int modCount = 0;// 记录list对象修改次数,list 每修改一次,modCount加1
#Itr源码
private class Itr implements Iterator<E> {
int cursor = 0;// 指针位置
int lastRet = -1;// 最后一次调用next或previous时,返回值所在的位置。如果调用remove时,lastRet=-1,保证了remove只能被调用一次。
int expectedModCount = modCount; //修改次数,初始值等于list的modCount。不管调用迭代器中的任何方法,必须保证 expectedModCount = modCount
final void checkForComodification() {// 检查迭代器的修改次数是否与list的修改次数相等。
// 如果不相等则抛出异常。所以在Itr类不允许迭代器循环时修改list的元素:while (itr.hasNext()) { itr.next(); arr.add("t"); } 此时会报java.util.ConcurrentModificationException错
if (modCount != expectedModCount)
throw new ConcurrentModificationException();
}
public boolean hasNext() {// 判断集合是否还存在元素
return cursor != size(); // 关键就是判断指针是否为集合最后端。
}
public E next() { // 获取集合的后面元素,实现逻辑:获取指针位置的元素值,指针位置后移一位。
checkForComodification();// 修改次数判断
try {
int i = cursor;
E next = get(i); // 获取指针位置的元素值,并return
lastRet = i; // 将LastRet设置为最后一次next获取的元素位置
cursor = i + 1; // 指针后移
return next; // 返回获取的指针元素值
} catch (IndexOutOfBoundsException e) {
checkForComodification();
throw new NoSuchElementException();
}
}
public void remove() { // 删除最后一次next或previous时的元素,实现逻辑:调用list.remove(lastRet)方法,指针上移一位
if (lastRet < 0) // 保证了一个next或previous方法后只能调用一次 remove方法。
throw new IllegalStateException();
checkForComodification(); // 修改次数判断
try {
AbstractList.this.remove(lastRet); // 调用list的remove方法删除最后一次next或previous时的元素
if (lastRet < cursor)
cursor--; // 指针上移一位
lastRet = -1; // lastRet设置为 -1,从而限制remove只能操作一次
expectedModCount = modCount; // 重新设置修改次数,保证与list中的修改次数一致。
} catch (IndexOutOfBoundsException e) {
throw new ConcurrentModificationException();
}
}
}
}
3.2ListItr类
ListItr类继承Itr类、实现ListIterator接口,实现了add、set、previous等方法逻辑:
- previous:指针先上移一位,再获取指针元素值
- add、set:调用list对应方法,lastRet 赋值:lastRet=-1;保证方法只能调用一次。
- nextIndex、previousIndex:分别获取指针位置或指针上一个位置的索引下标。
同Itr类原理相同,方法调用时先调用checkForComodification( )方法进行检查,最后保证expectedModCount==moCount。
在调用迭代器时不能对list对象进行修改操作;add、set、remove只能调用一次。
public abstract class AbstractList<E> extends AbstractCollection<E> implements List<E> {
protected transient int modCount = 0;// 记录list对象修改次数,list 每修改一次,modCount加1
#ListItr源码
private class Itr implements Iterator<E> {
int cursor = 0;// 指针位置
int lastRet = -1;// 最后一次调用next或previous时,返回值所在的位置。如果调用remove时,lastRet=-1,保证了remove只能被调用一次。
int expectedModCount = modCount; //修改次数,初始值等于list的modCount。不管调用迭代器中的任何方法,必须保证 expectedModCount = modCount
final void checkForComodification() {// 检查迭代器的修改次数是否与list的修改次数相等。
if (modCount != expectedModCount)
throw new ConcurrentModificationException();
}
...
}
private class ListItr extends Itr implements ListIterator<E> {
ListItr(int index) { // 构造方法,获取从特定位置开始的迭代器
cursor = index;
}
public boolean hasPrevious() { // 判断集合前面是否还有元素
return cursor != 0;
}
public E previous() { // 获取集合前面的元素,实现逻辑:指针上移一位,获取指针位置的元素值
checkForComodification(); // 保证修改次数相等
try {
int i = cursor - 1; // 指针上移一位
E previous = get(i); // 获取指针所在位置的元素
lastRet = cursor = i; // 赋值
return previous; // 返回上一位的元素值
} catch (IndexOutOfBoundsException e) {
checkForComodification();
throw new NoSuchElementException();
}
}
public int nextIndex() { // 获取指针位置索引
return cursor;
}
public int previousIndex() { //获取指针前一个位置索引
return cursor-1;
}
public void set(E e) { // 将最后一次调用next或previous时的位置重新赋值,实现逻辑:调用list.set(lastRet, e)重新赋值。
if (lastRet < 0)
throw new IllegalStateException();
checkForComodification();
try {
AbstractList.this.set(lastRet, e); // 调用list.set(lastRet, e)重新赋值
expectedModCount = modCount;// 修改次数
} catch (IndexOutOfBoundsException ex) {
throw new ConcurrentModificationException();
}
}
public void add(E e) {// 添加元素,实现逻辑:调用list.add(i, e)添加元素,指针后移一位
checkForComodification();
try {
int i = cursor;
AbstractList.this.add(i, e); // 调用list.add(i, e)添加元素
lastRet = -1; // lastRet设置为-1,保证next或previos后只能调用一次add方法
cursor = i + 1; // 指针后移一位
expectedModCount = modCount; // 保证修改次数相等
} catch (IndexOutOfBoundsException ex) {
throw new ConcurrentModificationException();
}
}
}
}