JAVA容器-迭代器Iterator详解
JAVA容器系列专题
【JAVA容器概论】
【JAVA容器-List接口容器详解】
【JAVA容器-迭代器Iterator详解】
【JAVA容器-Set接口容器详解】
【JAVA容器-Map接口容器详解】
【JAVA容器演进及应用(含对Disruptor的浅解)】
前置知识
核心源码解读
public class ArrayList<E>{
// ArrayList内部迭代器类
private class Itr implements Iterator<E> {
// int类型指针
int cursor;
// 操作的元素的索引
int lastRet = -1;
// 判断伪指针是否已经指向最后一个元素,是则返回false
public boolean hasNext() {
return cursor != size;
}
// 返回集合下一个元素
public E next() {
// 首先获取当前伪指针所在索引的快照
int i = cursor;
// 保存底层数组的当前快照
Object[] elementData = ArrayList.this.elementData;
// 将指针自增,指向下一个元素
cursor = i + 1;
// 获取伪指针快照指向的元素
return (E) elementData[lastRet = i];
}
// 删除当前指针元素
public void remove() {
if (lastRet < 0)
throw new IllegalStateException();
try {
ArrayList.this.remove(lastRet);
cursor = lastRet;
lastRet = -1;
expectedModCount = modCount;
} catch (IndexOutOfBoundsException ex) {
throw new ConcurrentModificationException();
}
}
}
}
使用Iterator出现问题
测试代码
public class TestItr {
public static void main(String[] args) {
ArrayList<String> list = new ArrayList<>();
list.add("aa");
list.add("bb");
list.add("cc");
list.add("dd");
list.add("ee");
//Iterator遍历过程中,在"cc"之后添加一个字符串"kk"
Iterator<String> it = list.iterator();
while(it.hasNext()){
if("cc".equals(it.next())){
list.add("kk");
}
}
}
}
执行结果
结论:
发现在使用迭代器遍历集合过程中,如果直接操作集合添加或者更新元素,会抛出异常。
引入ListIterator
引入ListIterator的原因:
- Itr在遍历过程中不能操作集合添加或更新元素。
- Itr不能逆向遍历或者循环遍历。
核心源码解读
private class ListItr extends Itr implements ListIterator<E> {
// 可传入int参数设置迭代器的起始位置
ListItr(int index) {
super();
cursor = index;
}
// 判断是否有上一个元素
public boolean hasPrevious() {
return cursor != 0;
}
// 返回指针的下一个索引值
public int nextIndex() {
return cursor;
}
// 返回指针的上一个索引值
public int previousIndex() {
return cursor - 1;
}
// 返回上一个元素,与hasNext()类似
public E previous() {
int i = cursor - 1;
Object[] elementData = ArrayList.this.elementData;
cursor = i;
return (E) elementData[lastRet = i];
}
// 更新当前指针所在位置的元素内容,原理还是调用的ArrayList的set方法
public void set(E e) {
try {
ArrayList.this.set(lastRet, e);
} catch (IndexOutOfBoundsException ex) {
throw new ConcurrentModificationException();
}
}
// 在指定索引位置添加元素
public void add(E e) {
try {
int i = cursor;
ArrayList.this.add(i, e);
cursor = i + 1;
lastRet = -1;
} catch (IndexOutOfBoundsException ex) {
throw new ConcurrentModificationException();
}
}
}
使用迭代器注意事项
测试代码
public class TestListItertor {
//这是main方法,程序的入口
public static void main(String[] args) {
ArrayList<String> list = new ArrayList<>();
list.add("aa");
list.add("bb");
list.add("cc");
list.add("dd");
list.add("ee");
//在"cc"之后添加一个字符串"kk"
ListIterator<String> it = list.listIterator();
while(it.hasNext()){
if("cc".equals(it.next())){
it.add("kk");
it.set("11");
}
}
System.out.println(it.hasNext());
System.out.println(it.hasPrevious());
//逆向遍历:
while(it.hasPrevious()){
System.out.println(it.previous());
}
System.out.println(it.hasNext());
System.out.println(it.hasPrevious());
System.out.println(list);
}
}
执行结果
反思
源码中,ListItr中的add()
和Itr中的remove()
都会重置lastRet=-1
,但是ListItr中的set()
需要用到lastRet更新元素,但是lastRet=-1,所以add()和remove()与set()连用时会报错
。
增强for循环原理(Itr迭代器)