zoukankan      html  css  js  c++  java
  • 设计模式之美学习-行为型-迭代器模式(三十一)

    什么是迭代器模式

    迭代器模式(Iterator Design Pattern),也叫作游标模式(Cursor Design Pattern)。

    一个完整的迭代器模式 设计容器(数组、链表、树、图、跳表)和迭代器

    为了达到基于接口而非实现编程的目的,容器又包含容器接口、容器实现类,迭代器又包含迭代器接口、迭代器实现类

    类图

     

     迭代器接口定义

    // 接口定义方式一
    public interface Iterator<E> {
        //是否还有迭代元素
        boolean hasNext();
        //指针向前移动一位
        void next();
        //返回当前指针指向的元素
        E currentItem();
    }
    
    // 接口定义方式二
    public interface Iterator<E> {
        //是否还有迭代元素
        boolean hasNext();
        //指针向前移动一位 并返回对象
        E next();
    }

    代码实现

    //迭代器模式的实现类
    public class ArrayIterator<E> implements Iterator<E> {
        private int cursor;
        private ArrayList<E> arrayList;
    
        public ArrayIterator(ArrayList<E> arrayList) {
            //当前指针
            this.cursor = 0;
            //迭代容器
            this.arrayList = arrayList;
        }
    
        @Override
        public boolean hasNext() {
            return cursor != arrayList.size(); //注意这里,cursor在指向最后一个元素的时候,hasNext()仍旧返回true。
        }
    
        @Override
        public void next() {
            //向前移动一位
            cursor++;
        }
    
        @Override
        public E currentItem() {
            //犯规当前指向元素
            if (cursor >= arrayList.size()) {
                throw new NoSuchElementException();
            }
            return arrayList.get(cursor);
        }
    }
    
    public class Demo {
        public static void main(String[] args) {
            ArrayList<String> names = new ArrayList<>();
            names.add("xzg");
            names.add("wang");
            names.add("zheng");
    
            //创建迭代器 并为迭代器设置容器
            Iterator<String> iterator = new ArrayIterator(names);
            while (iterator.hasNext()) {
                System.out.println(iterator.currentItem());
                iterator.next();
            }
        }
    }

    java容器代码实现

    java是容器实现获得迭代器的接口 在类里面new 迭代器

    //容器
    public interface List<E> {
        //定义获取迭代器接口
        Iterator iterator();
        //...省略其他接口函数...
    }
    
    public class ArrayList<E> implements List<E> {
        //...
        //返回迭代器
        public Iterator iterator() {
            return new ArrayIterator(this);
        }
        //...省略其他代码
    }
    
    public class Demo {
        public static void main(String[] args) {
            List<String> names = new ArrayList<>();
            names.add("xzg");
            names.add("wang");
            names.add("zheng");
    
            //使用  foreach语法糖 编译后也是通过迭代器
            Iterator<String> iterator = names.iterator();
            while (iterator.hasNext()) {
                System.out.println(iterator.currentItem());
                iterator.next();
            }
        }
    }

    好处是,将迭代和容器分离,复杂的容器可能会有多种迭代方式,如果都写在容器里面增加融洽的复杂度

    如:有各种复杂的遍历方式。比如,树有前中后序、按层遍历,图有深度优先、广度优先遍历等等

    迭代过程中删除和新增元素

    删除元素

    可能导致遍历不到的问题 以上面ArrayIterator 为例子

    public class Demo {
      public static void main(String[] args) {
        List<String> names = new ArrayList<>();
        names.add("a");
        names.add("b");
        names.add("c");
        names.add("d");
    
        Iterator<String> iterator = names.iterator();
        iterator.next();
        names.remove("a");
      }
    }

    情况一

    遍历前删除第一个元素 对遍历结果不会有影响

    情况二 

    当遍历到第二个元素 删除元素a 数组重排 导致遍历不到

    情况三

    遍历过程中 删除指针后面的元素 不会有影响

    插入元素 

    遍历到b 在a前面插入元素 导致a重复遍历

    如何解决重复遍历和未遍历不到问题

    方案1:遍历过程中不能新增删除元素

    方案2:遍历过程中 新增删除元素 遍历报错(java就是采用这种实现)

    java的解决方案

    在 ArrayList 中定义一个成员变量 modCount,记录集合被修改的次数,集合每调用一次增加或删除元素的函数,就会给 modCount 加 1。当通过调用集合上的 iterator() 函数来创建迭代器的时候,我们把 modCount 值传递给迭代器的 expectedModCount 成员变量,之后每次调用迭代器上的 hasNext()、next()、currentItem() 函数,我们都会检查集合上的 modCount 是否等于 expectedModCount,也就是看,在创建完迭代器之后,modCount 是否改变过

    public class ArrayIterator implements Iterator {
        private int cursor;
        private ArrayList arrayList;
        private int expectedModCount;
    
        public ArrayIterator(ArrayList arrayList) {
            this.cursor = 0;
            this.arrayList = arrayList;
            //存储arrayList的expectedModCount
            this.expectedModCount = arrayList.modCount;
        }
    
        @Override
        public boolean hasNext() {
            //检查集合是否改变
            checkForComodification();
            return cursor < arrayList.size();
        }
    
        @Override
        public void next() {
            //检查集合是否改变
            checkForComodification();
            cursor++;
        }
    
        @Override
        public Object currentItem() {
            //检查集合是否改变
            checkForComodification();
            return arrayList.get(cursor);
        }
    
        private void checkForComodification() {
            //集合发生改变报错
            if (arrayList.modCount != expectedModCount)
                throw new ConcurrentModificationException();
        }
    }
    
    //代码示例
    public class Demo {
        public static void main(String[] args) {
            List<String> names = new ArrayList<>();
            names.add("a");
            names.add("b");
            names.add("c");
            names.add("d");
    
            Iterator<String> iterator = names.iterator();
            iterator.next();
            names.remove("a");
            iterator.next();//抛出ConcurrentModificationException异常
        }
    }

    为什么通过迭代器可以删除元素

    迭代器类新增了一个 lastRet 成员变量,用来记录游标指向的前一个元素。通过迭代器去删除这个元素的时候,我们可以更新迭代器中的游标和 lastRet 值,来保证不会因为删除元素而导致某个元素遍历不到

    删除元素下标大于cusor什么都不做

    伤处元素下标小于cusor cusor-1  lastRet-1

  • 相关阅读:
    MySQL日志系统
    MySQL基础架构
    Java操作XML牛逼利器JDOM&DOM4J
    SAX方式解析XML
    DOM方式解析XML
    Jquery Ajax
    Jquery动画效果
    angular6新建项目
    mysql命令行使用
    git常用命令
  • 原文地址:https://www.cnblogs.com/LQBlog/p/12713238.html
Copyright © 2011-2022 走看看