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

  • 相关阅读:
    用wamp配置的环境,想用CMD连接mysql怎么连
    Mysql删除表
    MySQL创建表
    Leetcode 130. Surrounded Regions
    Leetcode 111. Minimum Depth of Binary Tree
    Leetcode 110. Balanced Binary Tree
    Leetcode 98. Validate Binary Search Tree
    Leetcode 99. Recover Binary Search Tree
    Leetcode 108. Convert Sorted Array to Binary Search Tree
    Leetcode 105. Construct Binary Tree from Preorder and Inorder Traversal
  • 原文地址:https://www.cnblogs.com/LQBlog/p/12713238.html
Copyright © 2011-2022 走看看