• 【转】Java迭代:Iterator和Iterable接口


    Java迭代 : Iterator和Iterable接口

    从英文意思去理解

    Iterable :故名思议,实现了这个接口的集合对象支持迭代,是可迭代的。able结尾的表示 能...样,可以做...。
    Iterator:   在英语中or 结尾是都是表示 ...样的人 or ... 者。如creator就是创作者的意思。这里也是一样:iterator就是迭代者,我们一般叫迭代器,它就是提供迭代机制的对象,具体如何迭代,都是Iterator接口规范的。
     
     
     

    Iterable

    一个集合对象要表明自己支持迭代,能有使用foreach语句的特权,就必须实现Iterable接口,表明我是可迭代的!然而实现Iterable接口,就必需为foreach语句提供一个迭代器。
    这个迭代器是用接口定义的 iterator方法提供的。也就是iterator方法需要返回一个Iterator对象。
    复制代码
    复制代码
    //Iterable JDK源码
    //可以通过成员内部类,方法内部类,甚至匿名内部类去实现Iterator

    public 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接口的约定,返回迭代器
                       
        }
        
    }
    复制代码
    复制代码
  • 相关阅读:
    复制文字时自动加版权
    Linux安装Docker
    ThreadLocal 理解
    Spring多数据源动态切换
    [LOJ#500]「LibreOJ β Round」ZQC的拼图
    [JLOI2015]装备购买
    「雅礼集训 2017 Day4」洗衣服
    [BJWC2011]元素
    [51nod1577]异或凑数
    [ARC101B]Median of Medians
  • 原文地址:https://www.cnblogs.com/guohaoyu110/p/6759096.html
Copyright © 2020-2023  润新知