使用Iterator遍历Collection
-
我们的Iterator是一个接口 ----- 可以称之为迭代器接口
-
Iterator接口的实现类的实例对象我们就称之为迭代器(迭代器其实就是设计模式的一种)
-
我们创建出的Iterator接口的实现类的实例对象就是为了遍历集合中的元素
GOF给迭代器模式定义为:提供一种方法访问一个容器对象中的各个元素,而又不需暴露该对象的内部细节.
- 也就是我们的迭代器模式(或者我们就称之为迭代器)就是为了容器而生
- 我们的迭代器就类似于"公交车售票员",“火车乘务员”,“空姐”,就是做一个检票的工作,一个元素一个元素都要进行检票(对应现实生活中每个人上车都要检票,看看是不是每个人都检票了,如果检票就上车)
如何获取迭代器?—也就是如何获取Iterator接口的实现类的实例化对象?
- 我们可以通过调用Collection接口中的iterator()方法来获取到Iterator接口的实现类的实例化对象
- Collection中的Iterator()方法是一个抽象方法,具体使用时是调用了Collection接口的具体实现类中的Iterator()方法来创建的迭代器
eg:
package 迭代器;
import java.util.Collection;
import java.util.ArrayList;
import java.util.Iterator;
public class IteratorDemo1 {
public static void main(String[] args) {
Collection c1 = new ArrayList();
c1.add(1234);
c1.add("abc");
/*
这里的Iterator就是指向我们的迭代器的一个引用,我们的Collection接口中的iterator()方法其实是一个抽象方法,这个时候我们其实是
调用了我们Collection接口的实现类的ArrayList类中的重写方法来的到了我们的迭代器(也就是Iterator接口的实现类的实例化对象)
*/
Iterator iterator = c1.iterator();
}
}
- 我们这个例子中是以多态的形式调用了Collection接口的实现子类ArrayList中重写过的iterator()方法
总结:
- 我们的迭代器对象的创建其实是调用了Collection接口的具体实现类中的Iterator()方法来实现的
- Collection接口中的Iterator()方法声明为了一个抽象方法
那么我们如何使用迭代器来遍历集合?
首先这里我们要使用到我们迭代器中的两个方法:
- hasNext();
- next();
- 这里我们提到了三种使用迭代器遍历集合的方式
方式一:
我们多次调用Iterator接口的实现类对象(也就是我们的迭代器)的next()方法
eg:
package 迭代器;
import java.util.Collection;
import java.util.ArrayList;
import java.util.Iterator;
public class IteratorDemo2 {
public static void main(String[] args) {
Collection c1 = new ArrayList();
c1.add(1234);
c1.add("abc");
c1.add(new Dog("小黑",2));
c1.add(false);
Iterator iterator = c1.iterator();
System.out.println(iterator.next());
System.out.println(iterator.next());
System.out.println(iterator.next());
System.out.println(iterator.next());
}
}
class Dog{
private String name;
private int age;
public Dog(){
}
public Dog(String name,int age){
this.name=name;
this.age= age;
}
public boolean equals(Object obj){
if(this==obj)return true;
else if(obj == null||this.getClass()!= obj.getClass())
return false;
else {
Dog dog = (Dog) obj;
if (dog.name.equals(name) && dog.age == age) {
return true;
}
return false;
}
}
@Override
public String toString() {
return "Dog{" +
"name='" + name + '\'' +
", age=" + age +
'}';
}
}
- 这里我们涉及到了一个Iterator中的一个next()方法,这个方法暂且可以认为我们从第一个位置开始遍历结合元素,这里我们可以认为我们的Iterator接口的实现类的实例化对象(也就是迭代器)就是一个指针,最开始我们创建迭代器对象的时候我们开始时迭代器对象(也就是我们的指针)就在我们集合元素中第一个元素的上面,也就是在空的地方指着,注意:这里我们的迭代器对象开始时不是在集合中第一个元素的位置,而是在我们的第一个元素的位置的上一个位置,也就是我们新创建的迭代器对象是指在我们集合中第一个元素的前面的空位置上面
- 而我们的next()方法的工作原理是分为了两步:
- 指针下移(也就是我们的迭代器下移)
- 将下移以后的位置上的集合元素返回
- 所以为了我们开始时遍历的是第一个元素,这个时候我们也就可以更好的去理解我们的迭代器(也就是指针)为什么一开始是指在我们的集合中的第一个元素的上一个位置了
- 而我们的next()方法的工作原理是分为了两步:
注意:
在我们上面的例子中我们创建的Collection实现类对象中一共只有四个元素,这个时候如果我们再使用一次会出现什么情况?
- 这个时候如果类比以下我们的数组,如果是数组的话这个时候就会抛出一个ArrayIndexOutOfBoundException(数组下标越界异常)
- 我们在这里也会抛出一个异常,只不过不是抛出数组下标越界了,而是抛出一个NoSuchElementException(找不到元素)
方式二:
在第一个方式上加上for循环遍历
- 使用集合对象.size — 也就是集合元素个数控制for循环次数然后再for循环内部通过next()方法进行遍历
- 这个方式在一定程度上可以大大减少我们在遍历集合元素时出现NoSuchElementException(找不到元素)的可能性
eg:
package 迭代器;
import java.util.Collection;
import java.util.ArrayList;
import java.util.Objects;
import java.util.Iterator;
public class IteratorDemo3 {
public static void main(String[] args) {
Collection c1 = new ArrayList();
c1.add("abc");
c1.add(1234);
c1.add(new Dog2());
c1.add(false);
c1.add(1.3);
Iterator iterator = c1.iterator();
/*
这个时候我们就是使用for循环控制次数,然后在for循环内部使用next()方法遍历集合中的元素,一定程度上可以大大减少我们
出现NoSuchElementException(找不到元素)的可能性
*/
for(int i=0 ; i<c1.size() ; i++){
System.out.println(iterator.next());
}
}
}
class Dog2{
private String name;
private int age;
public Dog2(){
}
public Dog2(String name, int age) {
this.name = name;
this.age = age;
}
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
Dog2 dog2 = (Dog2) o;
return age == dog2.age &&
Objects.equals(name, dog2.name);
}
@Override
public int hashCode() {
return Objects.hash(name, age);
}
@Override
public String toString() {
return "Dog2{" +
"name='" + name + '\'' +
", age=" + age +
'}';
}
}
总结:
- 前面两种方式在我们实际编程之中很少能使用到,绝大多数情况下都是使用我们下面的方式三进行集合元素的遍历
方式三:
既然我们前面提到我们遍历集合中的两个方法,那么前面的方式一和方式二中都只设计到了next()方法,很显然我们要两个方法搭配使用,这个时候我们就提出了方式三
- 我们方式三就是使用while循环,然后让我们的hasNext()方法作为循环判断条件,然后再while循环内部再使用next()方法来遍历集合中的元素
eg:
package 迭代器;
import java.util.Collection;
import java.util.ArrayList;
import java.util.Objects;
import java.util.Iterator;
public class IteratorDemo4 {
public static void main(String[] args) {
Collection c1 = new ArrayList();
c1.add(1234);
c1.add("abc");
c1.add(false);
c1.add(new Dog3());
c1.add(1.5);
/*
这里我们创建出了一个迭代器对象
*/
Iterator iterator = c1.iterator();
/*
这里我们使用方式三对我们的集合元素进行遍历,我们在实际的编程中大多都是使用这种方式遍历集合元素
并且我们使用这种方式可以完全避免出现NoSuchElementException(找不到元素)的情况
*/
while(iterator.hasNext()){
System.out.println(iterator.next());
}
}
}
class Dog3{
private String name;
private int age;
public Dog3(){
}
public Dog3(String name,int age){
this.name = name;
this.age = age;
}
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
Dog3 dog3 = (Dog3) o;
return age == dog3.age &&
Objects.equals(name, dog3.name);
}
@Override
public int hashCode() {
return Objects.hash(name, age);
}
@Override
public String toString() {
return "Dog3{" +
"name='" + name + '\'' +
", age=" + age +
'}';
}
}
- 那么方式三是如何执行的呐?
- 首先我们的while循环的退出条件是什么?—当我们的hasNext()方法执行为false时退出,我们的hasNext()方法何时返回false? ----hasNext()方法是判断我们的指针(迭代器)指向的元素的下一个位置是否还有元素,如果还有元素,返回一个true,如果下一个位置没有元素了,这个时候返回false,也就是当我们的迭代器指向的元素的下一个位置还有元素时我们就会进入到while循环中,然后使用next()方法就会遍历迭代器指向的下一个元素,一直遍历,当遍历到最后我们的hasNext()方法判断的下一个位置为null时,这个时候就退出while循环,这个时候从这个为null 的元素开始就不会再遍历了,这个时候也就避免了出现异常(NoSuchElementException(找不到元素))
总结:
通俗的讲:
- 我们的hasNext()方法就是判断我们的集合中还有没有没被遍历的元素,如果有就返回一个true
- 我们的next()方法就是遍历我们集合中的元素
具体的hasNext()方法和我们的next()方法的执行原理我们这篇文章中已经是讲解了,我们在后面的会再次具体分析