1.定义
提供一种方法顺序访问一个聚合对象中的各个元素,而又不暴露其内部的表示.
把遍历的功能封装到迭代器中,让数据结构管理数据,遍历的功能交给迭代器.
2.代码实现
jdk的util包中包含了迭代器接口,代码如下:
public interface Iterator<E> { /** * Returns {@code true} if the iteration has more elements. * (In other words, returns {@code true} if {@link #next} would * return an element rather than throwing an exception.) * * @return {@code true} if the iteration has more elements */ boolean hasNext(); /** * Returns the next element in the iteration. * * @return the next element in the iteration * @throws NoSuchElementException if the iteration has no more elements */ E next(); /** * Removes from the underlying collection the last element returned * by this iterator (optional operation). This method can be called * only once per call to {@link #next}. The behavior of an iterator * is unspecified if the underlying collection is modified while the * iteration is in progress in any way other than by calling this * method. * * @implSpec * The default implementation throws an instance of * {@link UnsupportedOperationException} and performs no other action. * * @throws UnsupportedOperationException if the {@code remove} * operation is not supported by this iterator * * @throws IllegalStateException if the {@code next} method has not * yet been called, or the {@code remove} method has already * been called after the last call to the {@code next} * method */ default void remove() { throw new UnsupportedOperationException("remove"); } /** * Performs the given action for each remaining element until all elements * have been processed or the action throws an exception. Actions are * performed in the order of iteration, if that order is specified. * Exceptions thrown by the action are relayed to the caller. * * @implSpec * <p>The default implementation behaves as if: * <pre>{@code * while (hasNext()) * action.accept(next()); * }</pre> * * @param action The action to be performed for each element * @throws NullPointerException if the specified action is null * @since 1.8 */ default void forEachRemaining(Consumer<? super E> action) { Objects.requireNonNull(action); while (hasNext()) action.accept(next()); } }
遍历最主要需要 next() 和 hasNext() 方法, remove方法用来删除元素.
假定现在有两种菜单,一种用数组实现,一种用ArrayList实现, 我们需要遍历这两种的数据, 因为这两种实现不同,所以遍历的时候我们需要两种遍历代码,一种是遍历数组,一种遍历 ArrayLIst
这时候我们就可以使用迭代器模式
我们先定义一个统一的菜单元素, 早餐 和 午餐都使用到了它
public class MenuItem { String name; String description; boolean Vegetarian; double price; public MenuItem(String name, String description, boolean vegetarian, double price) { super(); this.name = name; this.description = description; Vegetarian = vegetarian; this.price = price; } public String getName() { return name; } public void setName(String name) { this.name = name; } public String getDescription() { return description; } public void setDescription(String description) { this.description = description; } public boolean isVegetarian() { return Vegetarian; } public void setVegetarian(boolean vegetarian) { Vegetarian = vegetarian; } public double getPrice() { return price; } public void setPrice(double price) { this.price = price; } }
存放了四个变量,就是用来记录一个菜单的没什么特殊含义.
在定义一个菜单接口,用来获取迭代器
package iterator; import java.util.Iterator; public interface Menu { Iterator createIterator(); }
先定义煎饼,因为煎饼可以当作早餐,用 ArrayLIst 实现,并且实现了Menu接口,要返回一个迭代器
package iterator; import java.util.ArrayList; import java.util.Iterator; public class PancakeHouseMenu implements Menu{ ArrayList menuItems; public PancakeHouseMenu() { menuItems = new ArrayList(); addItem("A", "A", true, 2.00); addItem("B", "B", true, 3.00); addItem("C", "C", false, 112.00); addItem("D", "D", true, 3.00); } public void addItem (String name, String description, boolean vegetarian, double price) { menuItems.add(new MenuItem(name, description, vegetarian, price)); } public Iterator createIterator() { return menuItems.iterator(); } public ArrayList getMenuItems() { return menuItems; } }
ArrayList 已经实现了 Iterator 接口, 所以 在 createIterator 方法中 我们直接返回 ArrayList 的接口
接下来我们定义午餐
package iterator; import java.util.ArrayList; import java.util.Iterator; public class DinerMenu implements Menu{ static final int MAX_ITEMS = 6; int numberOfItems = 0; MenuItem[] menuItems; public DinerMenu() { menuItems = new MenuItem[MAX_ITEMS]; addItem("A", "A", true, 1.0); addItem("B", "B", true, 2.0); addItem("C", "C", true, 3.0); addItem("D", "D", true, 4.0); } public void addItem (String name, String description, boolean vegetarian, double price) { if(numberOfItems >= MAX_ITEMS) { System.out.println("menu is full"); } else { menuItems[numberOfItems] = new MenuItem(name, description, vegetarian, price); numberOfItems ++; } } public Iterator createIterator() { return new DinerMenuIterator(menuItems); } }
因为 数组没有iterator,所以需要我们自己定义
定义 午餐的 Iterator
package iterator; import java.util.Iterator; public class DinerMenuIterator implements Iterator{ MenuItem[] items; int position; public DinerMenuIterator(MenuItem[] items) { this.items = items; } @Override public boolean hasNext() { if (position >= items.length || items[position] == null) { return false; } return true; } @Override public Object next() { return items[position++]; } }
在午餐菜单中的createIterator方法中返回.
定义一个服务员遍历上面两种菜单
public class Waitress { //用来获取各种菜单,比如 早餐,午餐等 ArrayList menus; public Waitress(ArrayList menus) { this.menus = menus; } public void printMenu() { Iterator menuIterator = menus.iterator(); while (menuIterator.hasNext()) { Menu menu = (Menu) menuIterator.next(); printMenu(menu.createIterator()); } } private void printMenu(Iterator it) { while (it.hasNext()) { MenuItem menuItem = (MenuItem) it.next(); System.out.print(menuItem.getName() + " --- "); System.out.print(menuItem.getPrice() + " --- "); System.out.println(menuItem.getDescription()); } } }
这边我们可以看到,传入的菜单都是Menu接口,不依赖于具体的菜单,如果依赖具体的菜单的话新增加菜单就需要修改大量代码,而且通过 Iterator 我们可以遍历 菜单内的元素, 而不需要写很多循环.
这边稍微改进了以下,用一个list来接收各种菜单,这样在增加菜单的时候就不需要修改waitress的代码,做到了开闭原则.
测试
package iterator; import java.util.ArrayList; import java.util.Arrays; public class MenuTestDrive { public static void main(String[] args) { PancakeHouseMenu pancakeHouseMenu = new PancakeHouseMenu(); DinerMenu dinerMenu = new DinerMenu(); CafeMenu cafeMenu = new CafeMenu(); ArrayList list = new ArrayList(); list.add(pancakeHouseMenu); list.add(dinerMenu); list.add(cafeMenu); Waitress waitress = new Waitress(list); waitress.printMenu(); } }
--------breakfase--------- A --- 2.0 --- A B --- 3.0 --- B C --- 112.0 --- C D --- 3.0 --- D --------dinner--------- A --- 1.0 --- A B --- 2.0 --- B C --- 3.0 --- C D --- 4.0 --- D
3.总结
迭代器还是比较简单的一个模式, 接口中主要包含 next 和 hasNext方法即可. 我们平常也可以通过 加强 for 循环来遍历 Collection 框架中的集合, 如果我们想要遍历某个数组,直接实现 Iterator 接口即可.
对于开闭原则理解又加深了,在拓展功能或者修改功能时候要做到不修改以前的代码,这样的设计才是一个好的设计.