`

iterator与iterable关系

阅读更多
用Iterator模式实现遍历集合
Iterator模式是用于遍历集合类的标准访问方法。它可以把访问逻辑从不同类型的集合类中抽象出来,从而避免向客户端暴露集合的内部结构。
例如,如果没有使用Iterator,遍历一个数组的方法是使用索引:
for(int i=0; i<array.size(); i++) { ... get(i) ... }
而访问一个链表(LinkedList)又必须使用while循环:
while((e=e.next())!=null) { ... e.data() ... }
以上两种方法客户端都必须事先知道集合的内部结构,访问代码和集合本身是紧耦合,无法将访问逻辑从集合类和客户端代码中分离出来,每一种集合对应一种遍历方法,客户端代码无法复用。
更恐怖的是,如果以后需要把ArrayList更换为LinkedList,则原来的客户端代码必须全部重写

 

为解决以上问题,Iterator模式总是用同一种逻辑来遍历集合:
for(Iterator it = c.iterater(); it.hasNext(); ) { ... }
奥秘在于客户端自身不维护遍历集合的"指针",所有的内部状态(如当前元素位置,是否有下一个元素)都由Iterator来维护,而这个Iterator由集合类通过工厂方法生成,因此,它知道如何遍历整个集合。
客户端从不直接和集合类打交道,它总是控制Iterator,向它发送"向前","向后","取当前元素"的命令,就可以间接遍历整个集合。


首先看看java.util.Iterator接口的定义:
public interface Iterator { boolean hasNext(); Object next(); void remove(); }
依赖前两个方法就能完成遍历,典型的代码如下:
for(Iterator it = c.iterator(); it.hasNext(); ) { Object o = it.next(); // 对o的操作... }
在JDK1.5中,还对上面的代码在语法上作了简化(但是限于只读,如果需要remove,还是直接使用iterator):


// Type是具体的类型,如String。 for(Type t : c) { // 对t的操作... }
每一种集合类返回的Iterator具体类型可能不同,Array可能返回ArrayIterator,Set可能返回SetIterator,Tree可能返回TreeIterator,但是它们都实现了Iterator接口,因此,客户端不关心到底是哪种Iterator,它只需要获得这个Iterator接口即可,这就是面向对象的威力。

在遍历的过程中出现java.util.ConcurrentModificationException的问题参http://www.blogjava.net/EvanLiu/archive/2008/08/31/224453.html

 

如果想用foreach遍历自定义类的集合,自定义类通常需要实现implement iterable接口. 该接口定义了Iterator<T> iterator()方法. 有些时候这个iterator方法可以供用类里面的集合属性.iterator()返回.

 

所以我们首先要了解到iterator与iterable这2个接口的具体定义是什么:

首先附上这两个接口JDK中的定义:
package java.lang;
import java.util.Iterator;
public interface Iterable<T> {
    Iterator<T> iterator();
}

package java.util;
public interface Iterator<E> {
    boolean hasNext();
    E next();
    void remove();
}

可以看到这两个接口来自于不同的package中,实现Iterable接口可以使用foreach方法来遍历。  但Iterable中只是用一个方法来返回一个Iterator类型实例。

示例:

public class Teacher implements Iterable
      
     private String name; 
     private int age; 
      
      
     public String getName() { 
         return name; 
     } 
     public void setName(String name) { 
         this.name = name; 
     } 
     public int getAge() { 
         return age; 
     } 
     public void setAge(int age) { 
         this.age = age; 
     } 
     public Iterator iterator() { 
         return new Itr(); 
     } 
  
      
     public static void main(String[] args) { 
         Teacher t = new Teacher(); 
         t.setName("aaaaaaa"); 
         t.setAge(23); 
         for (Object o : t) { 
             System.out.println(o.toString()); 
         } 
     } 
      
      
     private class Itr implements Iterator
         private int cursor=0; // 属性的索引 
         private Field[] fields = Teacher.class.getDeclaredFields(); // 属性的数组 
          
         public boolean hasNext() { 
  
             return cursor!=(Teacher.class.getDeclaredFields().length); 
         } 
  
         public Object next() { 
             Object o=null; 
             try{ 
                 fields[cursor].setAccessible(true);  // 让内部类可以访问外部类的私有属性的值 
                 o = fields[cursor].getName()+" "+fields[cursor].get(Teacher.this); 
                 cursor++; 
             }catch(Exception e){ 
                 System.out.println(e); 
             } 
              
             return o; 
         } 
  
         public void remove() { 
             // TODO Auto-generated method stub 
              
         } 
          
     } 
 }

 

示例2:

import java.util.Iterator;

//测试类
public class Exec {
    public static void main(String[] args) throws Exception {
        // 创建学生集合类
        Students students = new Students(10);

        // 通过for each语句迭代循环学生集合类的每个元素
        for (Object obj : students) {
            Student stu = (Student) obj;
            System.out.println(stu.getSid() + ":" + stu.getName());
        }
    }
}

// 支持for each迭代循环的学生集合类
class Students implements Iterable {
    // 存储所有学生类的数组
    private Student[] stus;

    // 该构造函数可以生成指定大小的学生类变量数组,并初始化该学生类变量数组
    public Students(int size) {
        stus = new Student[size];
        for (int i = 0; i < size; i++) {
            stus[i] = new Student(String.valueOf(i), "学生" + String.valueOf(i));
        }
    }

    // 实现Iterable接口的重要方法,返回自定义的迭代类变量
    public Iterator iterator() {
        return new StudentIterator();
    }

    // 实现Iterator接口的私有内部类,外界无法直接访问
    private class StudentIterator implements Iterator {
        // 当前迭代元素的下标
        private int index = 0;

        // 判断是否还有下一个元素,如果迭代到最后一个元素就返回false
        public boolean hasNext() {
            return index != stus.length;
        }

        // 返回当前元素数据,并递增下标
        public Object next() {
            return stus[index++];
        }

        // 这里不支持,抛出不支持操作异常
        public void remove() {
            throw new UnsupportedOperationException();
        }
    }
}

// 学生类
class Student {
    // 学生学号
    private String sid;

    // 学生姓名
    private String name;

    // 默认构造函数
    public Student() {
    }

    // 支持属性值初始化的构造函数
    public Student(String sid, String name) {
        setSid(sid);
        setName(name);
    }

    // 学生学号的读取函数
    public String getSid() {
        return sid;
    }

    // 学生学号的设置函数
    public void setSid(String sid) {
        this.sid = sid;
    }

    // 学生姓名的读取函数
    public String getName() {
        return name;
    }

    // 学生姓名的设置函数
    public void setName(String name) {
        this.name = name;
    }

    // 格式化字符信息输出
    public String toString() {
        return this.sid + ":" + this.name;
    }
}

 

如上面的例子描述,JAVA中得各种集合实际上都对Iterator接口的3个方法重新覆盖了,而如果要使用foreach方法来遍历,那么就必须实现Iterable接口,一般实现方式就如上,实际上可以用匿名内部类简化上面的操作:

public Iterator iterator() {
        return new Iterator ()
       {
           private int index = 0;

           public boolean hasNext() {
                 return index != stus.length;
           }

           public Object next() {
               return stus[index++];
           }

           public void remove() {
              throw new UnsupportedOperationException();
          }
      };
 }

 

为什么一定要去实现Iterable这个接口呢?为什么不直接实现Iterator接口呢?

看一下JDK中的集合类,比如List一族或者Set一族,
都是实现了Iterable接口,但并不直接实现Iterator接口。
仔细想一下这么做是有道理的。因为Iterator接口的核心方法next()或者hasNext()
依赖于迭代器的当前迭代位置的。
如果Collection直接实现Iterator接口,势必导致集合对象中包含当前迭代位置的数据(指针)。
当集合在不同方法间被传递时,由于当前迭代位置不可预置,那么next()方法的结果会变成不可预知。
除非再为Iterator接口添加一个reset()方法,用来重置当前迭代位置。
但即时这样,Collection也只能同时存在一个当前迭代位置。
而Iterable则不然,每次调用都会返回一个从头开始计数的迭代器。
多个迭代器是互不干扰的。

 

在JAVA SE5中,大量的类都是Iterable类型,主要包括所有的Collection类(不包括各种Map)

比如JAVA源码中得Collection接口定义如下:

public interface Collection<E> extends Iterable<E> {
    int size();
    boolean isEmpty();
    boolean contains(Object o);
    Iterator<E> iterator();
    Object[] toArray();
    <T> T[] toArray(T[] a);

    // Modification Operations
    boolean add(E e);
    boolean remove(Object o);
    // Bulk Operations
    boolean containsAll(Collection<?> c);
    boolean addAll(Collection<? extends E> c);
    boolean removeAll(Collection<?> c);
    boolean retainAll(Collection<?> c);
    boolean equals(Object o);
    int hashCode();
}

 

 

 

分享到:
评论

相关推荐

Global site tag (gtag.js) - Google Analytics