11.迭代器模式

1.什么是迭代器模式?

迭代器模式提供一个方法顺序访问一个聚合对象中的各个元素,而又不暴露其内部的实现。

2.通过实例需求理解迭代器模式

2.1 项目总监Review到问题

在做项目的时候,技术总监在review代码的时候发现了一个奇特的现象,两个员工对一个实体类进行封装时的用法不一样,一个使用List对实体对象进行存储,一个直接使用实体对象数组的形式,导致使用者必须针对两种不同的表示写出两段代码来适应。
如下所示:
在这里插入图片描述

从下面的代码我们可以看到,对于遍历DinerMenu和PancakeHouseMenu里的MenuItem的做法,由于使用不同的方式存储的,所以在使用的时候需要针对性的编写代码进行使用,这样很麻烦,不是吗?

package IteratorPattern.first;
import java.util.List;
public class FirstTest {
    public static void main(String[] args) {
        //针对DinerMenu的用法
        DinerMenu dinerMenu = new DinerMenu();
        MenuItem[] menuItems = dinerMenu.getMenuItems();
        for(int i=0;i<dinerMenu.numberOfItems;i++){
            System.out.println(menuItems[i].getName());
        }

        //针对PancakeHouseMenu的用法
        PancakeHouseMenu pancakeHouseMenu = new PancakeHouseMenu();
        List list = pancakeHouseMenu.getMenuItems();
        for(Object obj:list){
            MenuItem menuItem = (MenuItem) obj;
            System.out.println(menuItem.getName());
        }
    }
}

这时,技术总监把这两个员工叫了过来,向他们说明了情况,他们也觉得这样对使用者很不友好,但是他们不知道该怎么去做(谁都不想改自己写好的代码)。
代码地址如下:
设计模式/src/main/java/IteratorPattern/first · 严家豆/设计模式 - 码云 - 开源中国 (gitee.com)

这时总监说到: 你们听说过迭代器模式吗? 迭代器模式提供一个方法顺序访问一个聚合对象中的各个元素,而又不暴露其内部的实现。

这样,我给你们一个接口,你们自己根据这个接口去修改代码,让使用者只需要了解这个接口就能轻易的使用你们的类:
在这里插入图片描述
两个员工无奈的接受了总监的意见,回去商量具体的方案了。

2.2 通过迭代器接口统一遍历方式

两个员工回去一起商量了下,于是迭代器模式的使用实例出来了:
在这里插入图片描述

如果按照上面的方式进行编写代码,那么使用者的代码就可以这样写:

import IteratorPattern.base.Iterator;
import IteratorPattern.first.MenuItem;

public class Alice {

    /**
     * 通过迭代器完成遍历
     */
    public void printMenuByIterator(){
        PancakeHouseMenu pancakeHouseMenu = new PancakeHouseMenu();
        Iterator pancakeIterator = pancakeHouseMenu.createIterator();
        DinerMenu dinerMenu = new DinerMenu();
        Iterator dinerIterator = dinerMenu.createIterator();
        printMenu(pancakeIterator);
        printMenu(dinerIterator);
    }

    /**
     * 通过统一的迭代器方式进行遍历
     * @param iterator
     */
    private void printMenu(Iterator iterator){
        while (iterator.hasNext()){
            MenuItem item = (MenuItem) iterator.next();
            System.out.println(item.getName()+" "+item.getPrice());
            System.out.println(item.getDescription());
        }
    }
}

嗯嗯,技术总监满意的点了点头。 请针对下面的代码对迭代器模式再进行理解一次:
迭代器模式提供一个方法顺序访问一个聚合对象中的各个元素,而又不暴露其内部的实现。

代码地址:
设计模式/src/main/java/IteratorPattern/second · 严家豆/设计模式 - 码云 - 开源中国 (gitee.com)

这时,我们可以非常激动了,因为我们可以去看JDK中集合框架中的迭代器了!!!

2.3 JDK中的迭代器

首先,我们来看一下JDK中util包中对迭代器接口的定义:
在这里插入图片描述
我们可以看到,JDK的迭代器接口使用了泛型,这就意味着我们可以指定迭代器中的元素类型, 当我们指定类型时,在使用迭代器进行遍历时就可以不用进行转型操作了。

其中有四个方法接口:

  • hasNexe: 用来判断迭代器中是否有下一个元素
  • next: 获取下一个元素
  • remove: 删除目前为止迭代器返回的最后一个元素
  • forEachRemaining: 对剩余的每个元素执行给定的操作,直到所有元素都处理完毕或该操作引发异常

2.3.1 使用ArrayList中的迭代器实验

1)使用迭代器进行遍历

public static void main(String[] args) {
   ArrayList<Integer> arrayList = new ArrayList<>();
   arrayList.add(1);
   arrayList.add(2);
   arrayList.add(8);
   arrayList.add(5);
   Iterator iterator = arrayList.iterator();
   while (iterator.hasNext()){
       System.out.println(iterator.next());
   }
}

在这里插入图片描述

2)使用迭代器进行删除

public static void main(String[] args) {
   ArrayList<Integer> arrayList = new ArrayList<>();
   arrayList.add(1);
   arrayList.add(2);
   arrayList.add(8);
   arrayList.add(5);

   Iterator<Integer> iterator = arrayList.iterator();
   while (iterator.hasNext()){
       Integer num = iterator.next();
       if(num==8) iterator.remove();
   }
    System.out.println(arrayList);
}

在这里插入图片描述

3)使用迭代器对剩余的元素进行操作

public static void main(String[] args) {
   ArrayList<Integer> arrayList = new ArrayList<>();
   arrayList.add(1);
   arrayList.add(2);
   arrayList.add(8);
   arrayList.add(5);

   Iterator<Integer> iterator = arrayList.iterator();
   while (iterator.hasNext()){
       Integer num = iterator.next();
       if(num==8) {
           iterator.remove();
           iterator.forEachRemaining(s->{
               System.out.println(s+1);
           });
       }
   }
}

在这里插入图片描述

2.3.2 ArrayList迭代器源码阅读

好啦,下面我们进行小试牛刀去看看ArrayList对迭代器的实现是怎样的:
在这里插入图片描述

下面我们一个一个方法的进行解读:
1)hasNext方法

public boolean hasNext() {
    return cursor != size;
}

这里是通过将cursor和ArrayList维护大小的遍历size进行比对来判断是否还有下一个元素存在。

2)next方法

public E next() {
    checkForComodification();
    int i = cursor;
    if (i >= size)
        throw new NoSuchElementException();
    Object[] elementData = ArrayList.this.elementData;
    if (i >= elementData.length)
        throw new ConcurrentModificationException();
    cursor = i + 1;
    return (E) elementData[lastRet = i];
}

在获取下一个元素时,首先确保在使用迭代器的过程中不会有对此对象进行修改操作,否则抛出异常。
然后对cursor和size进行比较确保有效。
接着获得ArrayList的存放元素的数组elementData, 然后将cursor与elementData的长度进行比较确保有效。
然后取出位置为cursor的元素并将其值赋给lastRet.接着将cursor进行加一操作,返回元素。

3)remove方法

public void remove() {
    if (lastRet < 0)
        throw new IllegalStateException();
    checkForComodification();

    try {
        ArrayList.this.remove(lastRet);
        cursor = lastRet;
        lastRet = -1;
        expectedModCount = modCount;
    } catch (IndexOutOfBoundsException ex) {
        throw new ConcurrentModificationException();
    }
}

其实这个方法就是对lastReb索引上的元素进行删除操作。
其他的请大家自行查阅源码进行解读。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

程序员小牧之

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值