Java迭代 : Iterator和Iterable接口

来源:互联网 发布:网络赌托该不该举报 编辑:程序博客网 时间:2024/06/06 08:31
Iterable :故名思议,实现了这个接口的集合对象支持迭代,是可迭代的。able结尾的表示 能...样,可以做...。
Iterator:   在英语中or 结尾是都是表示 ...样的人 or ... 者。如creator就是创作者的意思。这里也是一样:iterator就是迭代者,我们一般叫迭代器,它就是提供迭代机制的对象,具体如何迭代,都是Iterator接口规范的。
 
 
 

Iterable

一个集合对象要表明自己支持迭代,能有使用foreach语句的特权,就必须实现Iterable接口,表明我是可迭代的!然而实现Iterable接口,就必需为foreach语句提供一个迭代器。
这个迭代器是用接口定义的 iterator方法提供的。也就是iterator方法需要返回一个Iterator对象。

 

复制代码
复制代码
//Iterable JDK源码//可以通过成员内部类,方法内部类,甚至匿名内部类去实现Iteratorpublic interface Iterable<T>{      Iterator<T> iterator();}
复制代码
复制代码

 

 

Iterator

 包含3个方法: hasNext ,  next , remove。remove按需求实现,一般它很少用到,以至于Eclipse接口方法自动补全时,都忽略了remove放方法。

1、每次在迭代前   ,先调用hasNext()探测是否迭代到终点(本次还能再迭代吗?)。
2、next方法不仅要返回当前元素,还要后移游标cursor
3、remove()方法用来删除最近一次已经迭代出的元素
4、 迭代出的元素是原集合中元素的拷贝(重要)
5、配合foreach使用

 

复制代码
复制代码
//Iterator接口的JDK源码,注释为整理建议使用Iterator的正确姿势public interface Iterator<E> {       boolean hasNext();    //每次next之前,先调用此方法探测是否迭代到终点    E next();            //返回当前迭代元素 ,同时,迭代游标后移                   /*删除最近一次已近迭代出出去的那个元素。     只有当next执行完后,才能调用remove函数。     比如你要删除第一个元素,不能直接调用 remove()   而要先next一下( );     在没有先调用next 就调用remove方法是会抛出异常的。     这个和MySQL中的ResultSet很类似    */    void remove()     {        throw new UnsupportedOperationException("remove");    }}
复制代码
复制代码

 

 

 

 迭代的具体细节

需要理解的地方

1、hasNext , next  , remove 的调用顺序

2、迭代出来的是原集合元素拷贝!

 

下面是手动迭代的例子,foreach的原理和它一样。

复制代码
复制代码
public static void main(String[] args){        List<Integer> li = new ArrayList<>();                li.add(1);        li.add(2);        li.add(3);                //不使用foreach 而手动迭代        Iterator<Integer> iter = li.iterator();    //获取ArrayList 的迭代器                while(iter.hasNext())                      //①先探测能否继续迭代        {            System.out.println(iter.next());       //②后取出本次迭代出的元素                     //invoke  remove()                     //③最后如果需要,调用remove                      }      }
复制代码
复制代码

 

 

AbstractList中实现的迭代器类,可以借鉴参考。

我们实现自己的迭代器的情况很少,毕竟JDK集合足够强大。

源码中有一些保护机制,为了便于理解我删改了。

复制代码
复制代码
private class Itr implements Iterator<E> {       /*               AbstractList 中实现的迭代器,删除了一些细节。不影响理解               Itr为一个priavate成员内部类              */             int cursor = 0;   //马上等待被迭代元素的index        //最近一次,已经被迭代出的元素的index,如果这个元素迭代后,被删除了,则lastRet重置为-1        int lastRet = -1;        public boolean hasNext() {            return cursor != size();      //当前游标值 等于  集合的size()  说明已经不能再迭代了。        }        public E next() {                int i = cursor;                E next = get(i);                lastRet = i;     //lastRet 保存的是最近一次已经被迭代出去的元素索引                cursor = i + 1;  //cursor为马上等待被迭代的元素的索引                return next;        }        public void remove()        {            if (lastRet < 0)                             //调用remove之前没有调用next                throw new IllegalStateException();       //则抛异常。这就是为什么在使用remove前,要next的原因            OuterList.this.remove(lastRet);            //从集合中删除这个元素            if (lastRet < cursor)                      //集合删除元素后,集合后面的元素的索引会都减小1,cursor也要同步后移                cursor--;            lastRet = -1;                                 //重置                   }}
复制代码
复制代码

 

 

 

迭代出来的元素都是原来集合元素的拷贝

Java集合中保存的元素实质是对象的引用(可以理解为C中的指针),而非对象本身。

迭代出的元素也就都是 引用的拷贝,结果还是引用。那么,如果集合中保存的元素是可变类型的,我们就可以通过迭代出的元素修改原集合中的对象。

而对于不可变类型,如String  基本元素的包装类型Integer 都是则不会反应到原集合中。

 

为了便于理解,画张图:

 

         

 

 

 

验证代码:

 

复制代码
复制代码
public class Main{    public static void main(String[] args)    {        List<Person> li = new ArrayList<>();                Person p = new Person("Tom");                li.add(p);                        for(Person ap: li)        {            ap.setName("Jerry");        }                System.out.println(li.get(0).getName());     //Jerry  not Tom            }}class Person{        public Person(String name)    {        this.name = (name==null?"":name);            }        private  String name;    public String getName()    {        return name;    }    public void setName(String name)    {        if(name == null)             name = "";        this.name = name;    }    }
复制代码
复制代码

 

 

 

 

小试牛刀,让自己的类支持迭代。

复制代码
复制代码
public class Main{    public static void main(String[] args)    {        MyString s = new MyString("1234567");                        for(char c:s)        {            System.out.println(c);        }            }}class MyString implements Iterable<Character>{        private int length = 0;    private String ineers = null;        public MyString(String s)    {        this.ineers = s;        this.length = s.length();            }            @Override    public Iterator<Character> iterator()    {                        class iter  implements Iterator<Character>     //方法内部类        {            private int cur= 0;                                    @Override            public boolean hasNext()            {                return cur != length;            }            @Override            public Character next()            {                                Character c = ineers.charAt(cur);                cur++;                return c;            }                        public void remove()            {                 // do nothing                             }        }        return new iter();     //安装Iterable接口的约定,返回迭代器                       }    }
复制代码
复制代码

 

0 0
原创粉丝点击