迭代器的深入理解
我们为什么要提出迭代器
因为我们要使用一个特有的专业的类去遍历Collection集合,这个时候我们就提出了迭代器
首先我们要知道迭代器是什么?
迭代器是一种模式,我们称之为迭代器模式
我们可以使用两个接口完成迭代器模式:
- Iterator接口 — 对Collection接口的实现子类都可以进行遍历
- ListIterator接口 – 只能对List接口实现子类进行遍历
- ListIterator接口专门是对List接口实现类遍历的迭代器,ListIterator接口中有更多方法可以去使用
那么我们如何使用迭代器模式去遍历Collection接口下的实现类?
我们在jdk1.2中提出了Iterator接口,通过这个接口我们就可以去实现遍历Collection,但是其实我们的Iterator只是一个接口,我们只是在这个接口中声明了一些抽象方法,具体并没有去实现这些方法,我们是将这些方法在具体的Collection接口实现类中去实现的,我们在Collection接口中申明了一个内部类Itr,这个内部类这个内部类就是实现我们Iterator类的内部类,我们在这个内部类中具体实现了我们的Iterator中的方法
我们的ArrayList类中具体实现的Iterator接口中的hasNext()方法为:
public boolean hasNext(){
return cursor!=size;
}
- 这里的cursor是一个光标,这个光标就是最开始时就只想第0个位置
- cursor起始位置为一个不合法位置(这个位置上没有元素)
- cursor起始指向第一个元素的上一个位置
当我们要遍历List接口的实现类时
这里我们就要使用ListIterator接口,这个接口专门就是为了遍历List集合的,我们的ListIterator接口中有多种对迭代器中的集合对象的元素的操作的方法
-
比如我们使用ListIterator就可以逆序进行集合的遍历
-
创建一个ListIterator接口的实现类对象,这里我们是通过List接口实现类对象调用ListIterator()方法就能得到一个ListItr类的对象,这类是一个内部类,这个类实现了ListIterator接口
- 其实这里创建迭代器对象的过程就是将我们的集合对象装到了迭代器中
- 注意:这里我们是要逆序进行输出,但是我们这个时候如果使用默认的构造方法创建出来的迭代器对象这个时候光标(cursor)的默认起始位置是0位置,但是我们要从后向前遍历,这个时候我们就要创建指定光标位置的ListIterator接口实现类的对象,这个时候我们的光标起始为位置应该要定义为:集合对象.size();
-
然后使用while循环,while循环判断条件为hasPrevious()方法的返回值
- hasPrevious()方法的底层实现
public boolean hasPrevious(){ return cursor!=0; //通过这里我们也就可以理解为什么我们逆序遍历时为什么光标初始位置为集合 //对象.size() }
小技巧: 我们正向遍历是从0位置遍历到 !=size, 那么我们的逆向遍历是遍历到 !=0,那么我们就可以计算出我们逆向遍历的起点是光标位置为size
-
在while循环内使用previous()方法来向前遍历
-
-
eg:
public class Demo1 { public static void main(String[] args) { ArrayList<String> a = new ArrayList(); a.add("abc"); a.add("abd"); a.add("abe"); a.add("abf"); /* 这里就是调用了有参的ListIterator()方法,并且实参为集合对象.size() */ ListIterator lis = a.listIterator(a.size()); /* 这里我们就可以发现我们的while循环的判断条件就是hasPrevious()方法的返回值 */ while(lis.hasPrevious()){ /* 这里我们就是使用previous()方法向前进行遍历 */ System.out.println(lis.previous()); } } }
这里的Iterator适用于Collection接口下的所有实现子类使用,而ListIterator只适用于List接口下的实现类使用
这里我们再说一个接口:Iterable接口
- 实现了这个接口的类都可以使用加强for循环进行遍历
关于我们使用迭代器模式遍历Collection接口实现类时可能出现的问题
-
如果我们使用迭代器对象正在操作迭代器对象中的集合,这个时候的这个集合的元素就不能被其他对象操作,不能发生改变
-
假如我们通过这个集合本身调用集合中的remove(int index)方法,这个时候就会抛出一个异常ConcurrentModificationException(并发修改异常)
public class Demo1 { public static void main(String[] args) { ArrayList<String> b = new ArrayList(); b.add("abc"); b.add("abd"); b.add("abe"); b.add("abf"); Iterator iter = b.iterator(); while(iter.hasNext()){ if(iter.next().equals("abc")){ /* 这个时候我们正在通过迭代器对对象进行操作,这个时候如果我们使用集合对象调用remove()方法尝试修改集合对象,这个时候就会抛出一个ConcurrentModificationException(并发修改异常) */ b.remove(2); } } } }
-
因为这个时候我们的迭代器在操作这个集合对象,我们改变这个集合元素就可能让我们的迭代器在操作这个集合对象时发生实际与预期不符
-
-
不能使用光标位置非法的迭代器对象调用迭代器的remove()方法
- 如果我们使用非法光标位置的迭代器对象去调用这个迭代器对象的remove()方法对这个迭代器中的集合进行操作时,这个时候就会抛出一个异常IllegalStateException()
eg:
public class Demo1 { public static void main(String[] args) { ArrayList<String> b = new ArrayList(); b.add("abc"); b.add("abd"); b.add("abe"); b.add("abf"); Iterator iter = b.iterator(); while(iter.hasNext()){ /* 这里我们的这个迭代器对象的光标正在第0个位置,这个时候第0个位置为非法位置,这个时候我们调用这个方法就是将我们当前光标指向的集合元素删除掉,这个时候由于是非法位置,这个位置上并没有元素,所以会抛出一个异常IllegalStateException(非法的状态异常) */ iter.remove(); } } }
- 当我们使用迭代器对象调用一次next()方法之后如果我们使用迭代器对象连续调用两次remove()方法,这个时候也会出现异常IllegalStateException(非法的状态异常)
- 当我们使用一次remove()方法之后我们的光标就会回到起始位置(也就是回到0的位置)
- 也就是我们使用一次remove()方法之后这个光标就会回到0位置,这个位置是一个不合法位置,这个时候我们再次调用remove()方法就会抛出一个异常IllegalStateException(非法状态异常)
- 当我们使用一次remove()方法之后我们的光标就会回到起始位置(也就是回到0的位置)