java 中迭代器的使用方法详解

java 中迭代器的使用方法详解

前言:

迭代器模式将一个集合给封装起来,主要是为用户提供了一种遍历其内部元素的方式。迭代器模式有两个优点:①提供给用户一个遍历的方式,而没有暴露其内部实现细节;②把元素之间游走的责任交给迭代器,而不是聚合对象,实现了用户与聚合对象之间的解耦。

迭代器模式主要是通过Iterator接口来管理一个聚合对象的,而用户使用的时候只需要拿到一个Iterator类型的对象即可完成对该聚合对象的遍历。这里的聚合对象一般是指ArrayList,LinkedList和底层实现为数组等拥有一组相同或相似特性的对象。通过迭代器模式对聚合对象的遍历主要是通过Iterator接口的next(),hasNext()方法进行的,这里next()方法将返回当前遍历点的元素值,而hasNext()方法则表征当前遍历点之后还有没有元素。Iterator接口中还有一个remove()方法,该方法将移除当前遍历点的元素。在一般情况下不需要使用该方法,一些特殊的情况可以调用该方法,如果当前聚合对象的遍历不支持该操作,那么可以在该方法中跑出UnSupportedOperationException。

这里我们以如下例子来对迭代器模式进行说明。现有两个餐厅的两套菜单,一套菜单是使用数组实现的,而另外一套菜单是使用ArrayList实现的。现在由于两个餐厅的合并而需要将两套菜单进行整合,由于双方的厨师都已经习惯了各自的菜单组装方式,因而都希望各自继续维护各自的菜单样式。但是,对于服务员来说,其为顾客提供菜单的时候则必须根据两套菜单进行两种不同方式的处理,这必然会增加服务员的工作难度,而且,如果后期有新的餐厅合并进来,比如其使用的菜单种类为HashMap,那么服务员将又会维护这一套菜单,这也不利于扩展。根据服务员的需求,其需要的是一个菜单列表,如果其面向的是各个不同的菜单类,那么势必会增加其工作难度,并且各个不同的菜单类中所提供的方法也不一定是服务员所需要的,因而,根据服务员的需求,这里需要制定一个菜单的规范,以实现服务员能够按照同一种方式对其进行遍历。这里就可以使用到迭代器模式,服务员只需要面向迭代器接口进行遍历,而各个厨师所拥有的菜单只需要实现该迭代器即可,其依然可以按照各自的方式维护其菜单项。这样就实现了不同的菜单与服务员的解耦。以下是使用迭代器模式解决该问题的具体代码。

菜单接口(主要包含创建迭代器的方法):

public interface Menu<T> {
  Iterator<T> createIterator();
}

菜单项:

public class MenuItem {
  private String name;
  private String description;
  private boolean vegetarian;
  private double price;

  public MenuItem(String name, String description, boolean vegetarian, double price) {
    this.name = name;
    this.description = description;
    this.vegetarian = vegetarian;
    this.price = price;
  }

  public String getName() {
    return name;
  }

  public String getDescription() {
    return description;
  }

  public boolean isVegetarian() {
    return vegetarian;
  }

  public double getPrice() {
    return price;
  }
}

菜单类(菜单项的组装方式):

public class DinerMenu implements Menu<MenuItem> {
  private static final int MAX_ITEMS = 6;
  private int numberOfItems = 0;
  private MenuItem[] menuItems;

  public DinerMenu() {
    menuItems = new MenuItem[MAX_ITEMS];
    addItem("Vegetarian BLT", "(Fakin') Bacon with lettuce & tomato on whole wheat", true, 2.99);
    addItem("BLT", "Bacon with lettuce & tomato on whole wheat", false, 2.99);
    addItem("Soup of the day", "Soup of the day, with a side of potato salad", false, 3.29);
    addItem("Hotdog", "A hot dog, with saurkraut, relish, onions, topped with cheese", false, 3.05);
  }

  public void addItem(String name, String description, boolean vegetarian, double price) {
    MenuItem menuItem = new MenuItem(name, description, vegetarian, price);
    if (numberOfItems >= MAX_ITEMS) {
      System.out.println("Sorry, menu is full, Can't add item to menu");
    } else {
      menuItems[numberOfItems] = menuItem;
      numberOfItems++;
    }
  }

  @Deprecated
  public MenuItem[] getMenuItems() {
    return menuItems;
  }

  public Iterator<MenuItem> createIterator() {
    return new DinerMenuIterator(menuItems);
  }
}
public class PancakeHouseMenu implements Menu<MenuItem> {
  private ArrayList<MenuItem> menuItems;

  public PancakeHouseMenu() {
    menuItems = new ArrayList<>();
    addItem("K&B's Pancake Breakfast", "Pancakes with scrambled eggs, and toast", true, 2.99);
    addItem("Regular Pancake Breakfast", "Pancakes with fried eggs, sausage", false, 2.99);
    addItem("Blueberry Pancakes", "Pancakes made with fresh blueberries", true, 3.49);
    addItem("Waffles", "Waffles, with your choice of blueberries or strawberries", true, 3.49);
  }

  public void addItem(String name, String description, boolean vegetarian, double price) {
    MenuItem menuItem = new MenuItem(name, description, vegetarian, price);
    menuItems.add(menuItem);
  }

  @Deprecated
  public ArrayList<MenuItem> getMenuItems() {
    return menuItems;
  }

  public Iterator<MenuItem> createIterator() {
    return menuItems.iterator();
  }
}

迭代器接口:

public interface Iterator<T> {
  boolean hasNext();
  T next();
}

迭代器类:

public class DinerMenuIterator implements Iterator<MenuItem> {
  private MenuItem[] items;
  private int position = 0;

  public DinerMenuIterator(MenuItem[] items) {
    this.items = items;
  }

  @Override
  public boolean hasNext() {
    return position < items.length && items[position] != null;
  }

  @Override
  public MenuItem next() {
    return items[position++];
  }

  @Override
  public void remove() {
    if (position <= 0) {
      throw new IllegalStateException("You can't remove an item until you've done at least one next()");
    }

    if (items[position - 1] != null) {
      for (int i = position - 1; i < items.length - 1; i++) {
        items[i] = items[i + 1];
      }
      items[items.length - 1] = null;
    }
  }
}
public class PancakeHouseIterator implements Iterator<MenuItem> {
  private ArrayList<MenuItem> items;
  private int position = 0;

  public PancakeHouseIterator(ArrayList<MenuItem> items) {
    this.items = items;
  }

  @Override
  public boolean hasNext() {
    return position < items.size();
  }

  @Override
  public MenuItem next() {
    return items.get(position++);
  }
}

服务员类:

public class Waitress {
  private Menu<MenuItem> pancakeHouseMenu;
  private Menu<MenuItem> dinerMenu;

  public Waitress(Menu<MenuItem> pancakeHouseMenu, Menu<MenuItem> dinerMenu) {
    this.pancakeHouseMenu = pancakeHouseMenu;
    this.dinerMenu = dinerMenu;
  }

  public void printMenu() {
    Iterator<MenuItem> pancakeIterator = pancakeHouseMenu.createIterator();
    Iterator<MenuItem> dinerIterator = dinerMenu.createIterator();
    System.out.println("MENU\n----\nBREAKFAST");
    printMenu(pancakeIterator);
    System.out.println("\nLUNCH");
    printMenu(dinerIterator);

  }

  private void printMenu(Iterator<MenuItem> iterator) {
    while (iterator.hasNext()) {
      MenuItem menuItem = iterator.next();
      System.out.print(menuItem.getName() + ", ");
      System.out.print(menuItem.getPrice() + " -- ");
      System.out.println(menuItem.getDescription());
    }
  }
}

从上面的代码可以看出,服务员并没有针对具体的菜单进行编程,而是依赖于一个创建菜单迭代器的Menu接口和一个迭代器接口Iterator来进行编程的,服务员并不需要知道所传过来的是何种组装方式的菜单,而只需要使用实现这两个接口的菜单对象进行遍历即可,这就达到了将变化的依赖于多态而实现接口,将不变的依赖于接口的目的,从而实现服务员与菜单组装方式的分离。

迭代器模式在Java类库的集合中随处可见,这里使用的Menu就相当于Java类库中的Iterable接口,其作用是创建一个迭代器对象,而Iterator接口和Java类库的Iterator接口基本一致。这里需要说明的是,实际上让一个类实现迭代器模式在为一个类增加功能的同时也增加了该类的维护负担,因为类的基本方法是高内聚的,所谓的内聚即是实现了一套相关的完整的功能,而迭代器接口实际上也是一套完整的相关的功能,因而让一个类实现迭代器模式隐含地为这个类增加了两套不那么“内聚”的两套功能,这就会导致该类在进行功能维护的时候需要兼顾双方。在Java类库中ArrayList和LinkedList中就有体现,其不仅提供了List所有的基本的remove方法,也提供了迭代器所需要实现的remove方法,为了实现两者的统一,其不得不作一些约定,比如遍历集合的时候不能调用该类基本的remove或者add等会更改该类结构的方法。

如有疑问请留言或者到本站社区交流讨论,感谢阅读,希望能帮助到大家,谢谢大家对本站的支持!

(0)

相关推荐

  • java 迭代器模式实例详解

    java 迭代器模式实例详解 今天来818设计模式中的迭代器模式,也是java中Stack,List,Set等接口以及数组这个数据结构都会使用的一种模式. 首先,为什么使用迭代器模式,目的就是通过一个通用的迭代方法,隐藏stack,list,set以及数组中不同的遍历细节.也就是说,我不想让那些调用我的遍历容器的方法的人知道我到底是怎么一个一个的获取这些元素的(stack的pop,list的get,数组的array[i]),我只想让他知道他能 通过一个迭代器Iterator或者通过一个for e

  • java集合迭代器Iterator中的remove陷阱

    package TestList; import java.util.ArrayList; import java.util.Iterator; import java.util.List; import java.util.TreeSet; public class TestIterator { /**      * @param args      */     public static void main(String[] args) {         // TODO Auto-gen

  • 详解Java中Iterator迭代器的用法

    迭代器(Iterator) 迭代器是一种设计模式,它是一个对象,它可以遍历并选择序列中的对象,而开发人员不需要了解该序列的底层结构.迭代器通常被称为"轻量级"对象,因为创建它的代价小. Java中的Iterator功能比较简单,并且只能单向移动: (1) 使用方法iterator()要求容器返回一个Iterator.第一次调用Iterator的next()方法时,它返回序列的第一个元素.注意:iterator()方法是java.lang.Iterable接口,被Collection继承

  • Java Iterator迭代器_动力节点Java学院整理

    迭代器是一种模式,它可以使得对于序列类型的数据结构的遍历行为与被遍历的对象分离,即我们无需关心该序列的底层结构是什么样子的.只要拿到这个对象,使用迭代器就可以遍历这个对象的内部. 1.Iterator Java提供一个专门的迭代器<<interface>>Iterator,我们可以对某个序列实现该interface,来提供标准的Java迭代器.Iterator接口实现后的功能是"使用"一个迭代器. 文档定义: Package java.util; publici

  • 详解Java中的迭代迭代器Iterator与枚举器Enumeration

    迭代器Iterator接口 1.迭代器接口 Iterable 内置方法iterator(), 返回一个新建的 Iterator. 如: public interface Iterable { Iterator Iterator(); } Iterator 有 hasNext() 和 next() 两个方法要实现. public interface Iterator { boolean hasNext(); Item next(); void remove(); //可选实现 } 2.实现 导入

  • Java实现的自定义迭代器功能示例

    本文实例讲述了Java实现的自定义迭代器功能.分享给大家供大家参考,具体如下: 编写自己的Iterator,实现Iterator接口,这里多说一句,实现Iterable后,可以用"foreach"循环遍历你的对象. import java.util.Iterator; import java.util.NoSuchElementException; /** * 演示Iterator和Iterable接口,并说明怎样编写一个用于对象数组的简单迭代器. */ public class Aa

  • java 中迭代器的使用方法详解

    java 中迭代器的使用方法详解 前言: 迭代器模式将一个集合给封装起来,主要是为用户提供了一种遍历其内部元素的方式.迭代器模式有两个优点:①提供给用户一个遍历的方式,而没有暴露其内部实现细节:②把元素之间游走的责任交给迭代器,而不是聚合对象,实现了用户与聚合对象之间的解耦. 迭代器模式主要是通过Iterator接口来管理一个聚合对象的,而用户使用的时候只需要拿到一个Iterator类型的对象即可完成对该聚合对象的遍历.这里的聚合对象一般是指ArrayList,LinkedList和底层实现为数

  • java 中enum的使用方法详解

    java 中enum的使用方法详解 enum 的全称为 enumeration, 是 JDK 1.5 中引入的新特性,存放在 java.lang 包中. 下面是我在使用 enum 过程中的一些经验和总结. 原始的接口定义常量 public interface IConstants { String MON = "Mon"; String TUE = "Tue"; String WED = "Wed"; String THU = "Thu

  • Java中ArrayList的removeAll方法详解

    本文介绍的是关于Java中ArrayList的removeAll方法的相关内容,分享出来供大家参考学习,下面来一起看看详细的介绍: 在开发过程中,遇到一个情况,就是从所有骑手Id中过滤没有标签的骑手Id(直接查询没有标签的骑手不容易实现), List<Integer> allRiderIdList = new ArrayList(); // 所有的骑手,大致有23W数据 List<Integer> hasAnyTagRiderId = new ArrayList(); // 有标签

  • Java中的== 和equals()方法详解与实例

    Java中的== 和equals()方法: Java中的数据类型,可分为两类: 1.基本数据类型,也称原始数据类型. byte,short,char,int,long,float,double,boolean,他们之间的比较,应用双等号(==),比较的是他们的值. 2.引用数据类型(类) 当它们用(==)进行比较的时候,比较的是他们在内存中的存放地址,所以,除非是同一个new出来的对象,他们的比较后的结果为true,否则比较后结果为false. Java当中所有的类都是继承于Object这个基类

  • java中set接口使用方法详解

    java中的set接口有如下的特点: 不允许出现重复元素: 集合中的元素位置无顺序: 有且只有一个值为null的元素. 因为java中的set接口模仿了数学上的set抽象,所以,对应的数学上set的特性为: 互异性:一个集合中,任何两个元素都认为是不相同的,即每个元素只能出现一次. 无序性:一个集合中,每个元素的地位都是相同的,元素之间是无序的.集合上可以定义序关系,定义了序关系后,元素之间就可以按照序关系排序.但就集合本身的特性而言,元素之间没有必然的序. 空集的性质:空集是一切集合的子集 S

  • Java中终止线程的方法详解

    Java中终止线程的方式主要有三种: 1.使用stop()方法,已被弃用.原因是:stop()是立即终止,会导致一些数据被到处理一部分就会被终止,而用户并不知道哪些数据被处理,哪些没有被处理,产生了不完整的"残疾"数据,不符合完整性,所以被废弃.So, forget it! 2.使用volatile标志位 看一个简单的例子: 首先,实现一个Runnable接口,在其中定义volatile标志位,在run()方法中使用标志位控制程序运行 public class MyRunnable i

  • Java中常用加密/解密方法详解

    安全问题已经成为一个越来越重要的问题,在Java中如何对重要数据进行加密解密是本文的主要内容. 一.常用的加密/解密算法 1.Base64 严格来说Base64并不是一种加密/解密算法,而是一种编码方式.Base64不生成密钥,通过Base64编码后的密文就可以直接"翻译"为明文,但是可以通过向明文中添加混淆字符来达到加密的效果. 2.DES DES是一种基于56位密钥的对称算法,1976年被美国联邦政府的国家标准局确定为联邦资料处理标准(FIPS),随后在国际上广泛流传开来.现在DE

  • Java中Arraylist动态扩容方法详解

    前言 本文主要给大家介绍了关于Java中Arraylist动态扩容的相关内容,分享出来供大家参考学习,下面话不多说了,来一起看看详细的介绍吧. ArrayList 概述 ArrayList是基于数组实现的,是一个动态数组,其容量能自动增长.ArrayList不是线程安全的,只能用在单线程环境下.实现了Serializable接口,因此它支持序列化,能够通过序列化传输:实现了RandomAccess接口,支持快速随机访问,实际上就是通过下标序号进行快速访问:实现了Cloneable接口,能被克隆.

  • java中封装的实现方法详解

    1.封装是什么?以及为什么要进行封装? 通常情况下可以给成员变量赋值一些合法但不合理的数值,这种情况在编译阶段和运行阶段都不会报错或给出任何的提示信息,此数值虽然合法但与现实生活不符:为了避免上述问题的发生,就需要对成员变量进行密封包装处理来保证该成员变量的合法合理性,这种机制就叫做封装.封装可以被认为是一个保护屏障,防止该类的代码和数据被外部类定义的代码随机访问.要访问该类的代码和数据,必须通过严格的接口控制. 2.如何进行封装? (1)私有化成员变量,使用private关键字修饰: (2)提

  • Java中EnumSet代替位域代码详解

    本文研究的主要是Java中EnumSet代替位域的相关内容,具体介绍如下. 读书笔记<Effective Java 中文版 第2版> 位域表示法允许利用位操作,有效地执行先 union(联合)和 intersection(交集)这样的集合操作.但是位域有着int枚举常亮的所有缺点,甚至更多.当位域一数字形式打印时,翻译位域比翻译简单的int枚举常量要困难得多.甚至,要遍历位域表示的所有元素都没有很容易的方法. //Bit field enumeration constant - OBSOLET

随机推荐