Day25 迭代器之Iterator底层
一、迭代器
1、概念:
迭代器(Iterator)是一种用于遍历集合(Collection)元素的接口,它提供了统一的方式来访问集合中的元素,而不暴露集合的内部结构。通过迭代器,我们可以依次访问集合中的每个元素,进行遍历和操作。
2、使用步骤:
-
获取集合的迭代器:
Iterator<String> iterator = collection.iterator();
-
遍历集合:
使用while循环和hasNext()方法来判断是否还有下一个元素:
while (iterator.hasNext()) { String element = iterator.next(); // 对元素进行操作 }
-
删除元素:
在遍历过程中,可以使用迭代器的remove()方法来删除当前元素:
iterator.remove();
注意: 迭代器提供了一种安全且高效的遍历集合的方式,可以在遍历过程中对集合进行增删改查操作,而不会出现并发修改异常。迭代器还支持快速失败机制,即在遍历过程中如果集合结构发生变化(如添加或删除元素),会抛出ConcurrentModificationException
异常,以确保遍历的安全性。
二、Iterator底层源码
注意:研究源码,必须找场景!!!
//场景
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);
}
public abstract class AbstractList<E> extends AbstractCollection<E> implements List<E> {
//外部操作数
//作用:记录修改元素的次数(添加、删除会让该变量++)
protected transient int modCount = 0;//modCount - 5
}
public class ArrayList<E> extends AbstractList<E> implements List<E>{
//元素个数
private int size;//size - 4
//数据容器 - ["aaa","bbb","ccc","ddd",null,null,null,null,null,null]
transient Object[] elementData;
//e - ddd
public boolean add(E e) {
ensureCapacityInternal(size + 1);//判断是否扩容
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++;
if (minCapacity - elementData.length > 0)
grow(minCapacity);
}
public E remove(int index) {
rangeCheck(index);
modCount++;
E oldValue = elementData(index);
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
return oldValue;
}
public Iterator<E> iterator() {
return new Itr();
}
//ArrayList类的内部类 -- 实现了遍历元素的功能
private class Itr implements Iterator<E> {
int cursor; // 游标 - 4
int lastRet = -1; // 当前元素的下标 - 3
int expectedModCount = modCount;//内部操作数 - 5
public boolean hasNext() {
return cursor != size;//4 - 4
}
@SuppressWarnings("unchecked")
public E next() {
/**
思考题:为什么在获取元素时,会先判断外部操作数是否等于内部操作数
考虑到遍历元素时,如果添加或删除元素,会导致数据个数变化
遍历时就有可能出现脏数据,如果外部操作数和内部操作数不相同就以为数据不同意,就报错!
*/
checkForComodification();//判断外部操作数是否等于内部操作数,如果不等于就报错
int i = cursor;//i - 3
if (i >= size)
throw new NoSuchElementException();
//获取外部类的成员属性 -- elementData
Object[] elementData = ArrayList.this.elementData;
if (i >= elementData.length)
throw new ConcurrentModificationException();
cursor = i + 1;
//elementData -- 获取的是ArrayList(外部类的)elementData -> Object[] elementData;
//Object类型的元素强转为集合中真实类型的元素
// --> elementData[3] --> Object类型的数据需要强转为String类型
return (E) elementData[lastRet = i];//elementData[3]
}
final void checkForComodification() {
if (modCount != expectedModCount)
throw new ConcurrentModificationException();
}
//重写了Iterator接口中的remove()
public void remove() {
//判断当前元素的下标是否小于0
if (lastRet < 0)
throw new IllegalStateException();
//判断外部操作数是否和内部操作数一致,不一致就会报错
checkForComodification();
try {
//利用ArraList类的remove()去删除元素
ArrayList.this.remove(lastRet);
//把当前元素的下标赋值给游标
cursor = lastRet;
//把-1赋值给lastRet
lastRet = -1;
//重新把外部操作数赋值给内部操作数
expectedModCount = modCount;
} catch (IndexOutOfBoundsException ex) {
throw new ConcurrentModificationException();
}
}
}
}
/**
思考题:为什么迭代器是一个接口,不是类???
因为Java提供了很多的集合,不同的集合实现增删改查的原理是不一样的,
所以,不同的集合都实现了各自遍历元素的代码(实现了各自的迭代器)
*/
//迭代器的接口
public interface Iterator<E> {
//判断是否有可迭代的元素
boolean hasNext();
//获取下一个元素
E next();
//删除元素的默认方法(作用:给实现类去重写,如果实现类可以选择不重写,意味着遍历时不能删除元素,当然实现类可以选择重写,那么就意味着这个迭代器可以在遍历元素时删除元素)
default void remove() {
//抛出异常 -- 删除异常
throw new UnsupportedOperationException("remove");
}
}
标签:操作数,elementData,Iterator,迭代,Day25,元素,遍历
From: https://blog.csdn.net/yjp1240201821/article/details/136989155