之前的总结:
https://blog.csdn.net/u011109881/article/details/59677544
个人觉得本章节,HeadFirst讲的没有之前看到的网站讲的清晰,至少在迭代器这部分是的;不过,head first书中讲的更详细些。本节还是采用Head First中的例子来描述迭代器模式。
情景再现
现有两家店的实现方式不同,一个采用ArrayList,一个采用数组;现需要对所有菜单遍历,那么做法一般会是如下形式:
首先菜单的model类:
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;
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;
}
@Override
public String toString() {
return "name =" + name + " description " + description
+ " isVegetarian " + vegetarian + " price= " + price;
}
}
两家店菜单的实现:
public class DinerMenu {
static final int MAX_ITEMS = 6;
public int numberOfItems = 0;
MenuItem[] menuItems;
public DinerMenu() {
menuItems = new MenuItem[MAX_ITEMS];
addMenuItem("Diner Menu1", "xxxxx", true, 3.99);
addMenuItem("Diner with meat", "xxxxx", false, 4.99);
addMenuItem("Diner Menu2", "xxxxx", true, 4.49);
addMenuItem("Diner with pork", "xxxx", false, 5.99);
}
public MenuItem[] getMenuItems() {
return menuItems;
}
public void addMenuItem(String name, String description,
boolean vegetarian, double price) {
MenuItem item = new MenuItem(name, description, vegetarian, price);
if (numberOfItems >= MAX_ITEMS) {
System.out.println("menu is full! Can't add item to menu any more");
} else {
menuItems[numberOfItems] = item;
numberOfItems++;
}
}
}
public class PancakeHouseMenu {
ArrayList<MenuItem> menuItems;
public PancakeHouseMenu() {
menuItems = new ArrayList<MenuItem>();
addMenuItem("Pancake Breakfast1", "xxxxx", true, 2.99);
addMenuItem("Pancake Breakfast with meat", "xxxxx", false, 3.99);
addMenuItem("Pancake Breakfast", "xxxxx", true, 3.49);
addMenuItem("Pancake Breakfast with pork", "xxxx", false, 4.99);
}
public ArrayList<MenuItem> getMenuItems() {
return menuItems;
}
public void addMenuItem(String name,String description,boolean vegetarian,double price) {
MenuItem item = new MenuItem(name, description, vegetarian, price);
menuItems.add(item);
}
}
输出类的普通实现:
public class Printer1 {
public static void main(String[] args) {
PancakeHouseMenu breakfastMenu = new PancakeHouseMenu();
ArrayList<MenuItem> breakfastList = breakfastMenu.getMenuItems();
for (MenuItem item : breakfastList) {
System.out.println(item.toString());
}
DinerMenu dinerMenu = new DinerMenu();
MenuItem[] dinerList = dinerMenu.getMenuItems();
for (int i = 0; i < dinerMenu.numberOfItems; i++) {
System.out.println(dinerList[i].toString());
}
}
}
这里可以看到,当实现种类有多少种,则循环需要执行多少次,也就意味着代码会根据实际情况变得复杂;要避免这种情况,就要用到本章的设计模式迭代器了。
添加迭代器
对于数组,没有现成的迭代器,需要自定义迭代器:(当然也可以继承util包里的Iterator)
package iterator;
public interface Iterator {
boolean hasNext();
Object next();
}
package iterator;
import bean.MenuItem;
public class DinerMenuIterator implements Iterator {
MenuItem[] items;
int pos = 0;
public DinerMenuIterator(MenuItem[] items) {
this.items = items;
}
public boolean hasNext() {
if (pos >= items.length || items[pos] == null) {
return false;
} else {
return true;
}
}
public Object next() {
MenuItem item = items[pos];
pos++;
return item;
}
}
对DinerMenu稍作修改,去掉getItems方法,添加获取Iterator方法
package bean;
import iterator.DinerMenuIterator;
public class DinerMenu {
static final int MAX_ITEMS = 6;
public int numberOfItems = 0;
MenuItem[] menuItems;
DinerMenuIterator iterator;//变动
public DinerMenu() {
menuItems = new MenuItem[MAX_ITEMS];
addMenuItem("Diner Menu1", "xxxxx", true, 3.99);
addMenuItem("Diner with meat", "xxxxx", false, 4.99);
addMenuItem("Diner Menu2", "xxxxx", true, 4.49);
addMenuItem("Diner with pork", "xxxx", false, 5.99);
iterator = new DinerMenuIterator(menuItems);//变动
}
//
// public MenuItem[] getMenuItems() {
// return menuItems;
// }
//
public void addMenuItem(String name, String description,
boolean vegetarian, double price) {
MenuItem item = new MenuItem(name, description, vegetarian, price);
if (numberOfItems >= MAX_ITEMS) {
System.out.println("menu is full! Can't add item to menu any more");
} else {
menuItems[numberOfItems] = item;
numberOfItems++;
}
}
//新增方法
public DinerMenuIterator getIterator() {
return this.iterator;
}
}
而对于breakfastMenu则简单的多,因为breakfastMenu实现是ArrayList,可以直接返回ArrayList的itorator。注意,此处代码和书中不同,此处的Iterator和上面自定义的Iterator并不是同一个Iterator
public class PancakeHouseMenu {
ArrayList<MenuItem> menuItems;
public PancakeHouseMenu() {
menuItems = new ArrayList<MenuItem>();
addMenuItem("Pancake Breakfast1", "xxxxx", true, 2.99);
addMenuItem("Pancake Breakfast with meat", "xxxxx", false, 3.99);
addMenuItem("Pancake Breakfast", "xxxxx", true, 3.49);
addMenuItem("Pancake Breakfast with pork", "xxxx", false, 4.99);
}
// public ArrayList<MenuItem> getMenuItems() {
// return menuItems;
// }
public void addMenuItem(String name,String description,boolean vegetarian,double price) {
MenuItem item = new MenuItem(name, description, vegetarian, price);
menuItems.add(item);
}
public Iterator<MenuItem> getIterator(){
return menuItems.iterator();
}
}
测试方法略作修改:
public class Printer2 {
public static void main(String[] args) {
PancakeHouseMenu breakfastMenu = new PancakeHouseMenu();
Iterator<MenuItem> iterator1 = breakfastMenu.getIterator();
while (iterator1.hasNext()) {
System.out.println(iterator1.next());
}
DinerMenu dinerMenu = new DinerMenu();
iterator.Iterator iterator2 = dinerMenu.getIterator();
while (iterator2.hasNext()) {
System.out.println(iterator2.next());
}
}
}
看上去形式统一了呢。
此时的类结构:
再改进:全部使用util的Iterator
Java util包中的Iterator和我们自定义的Iterator其实是类似的,之所以先用自定义的Iterator,是因为这样做可以更了解Java的Iterator的实现原理
Java自带的Itorator:
package java.util;
import java.util.function.Consumer;
/**
* An iterator over a collection. {@code Iterator} takes the place of
* {@link Enumeration} in the Java Collections Framework. Iterators
* differ from enumerations in two ways:
*
* <ul>
* <li> Iterators allow the caller to remove elements from the
* underlying collection during the iteration with well-defined
* semantics.
* <li> Method names have been improved.
* </ul>
*
* <p>This interface is a member of the
* <a href="{@docRoot}/../technotes/guides/collections/index.html">
* Java Collections Framework</a>.
*
* @param <E> the type of elements returned by this iterator
*
* @author Josh Bloch
* @see Collection
* @see ListIterator
* @see Iterable
* @since 1.2
*/
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());
}
}
可以看到,主体和自定义的Iterator还是类似的。
对于PancakeHouseMenu基本没有变动
public class PancakeHouseMenu {
ArrayList<MenuItem> menuItems;
public PancakeHouseMenu() {
menuItems = new ArrayList<MenuItem>();
addMenuItem("Pancake Breakfast1", "xxxxx", true, 2.99);
addMenuItem("Pancake Breakfast with meat", "xxxxx", false, 3.99);
addMenuItem("Pancake Breakfast", "xxxxx", true, 3.49);
addMenuItem("Pancake Breakfast with pork", "xxxx", false, 4.99);
}
public void addMenuItem(String name,String description,boolean vegetarian,double price) {
MenuItem item = new MenuItem(name, description, vegetarian, price);
menuItems.add(item);
}
public Iterator<MenuItem> createIterator(){
return menuItems.iterator();
}
}
DinerMenuIterator的基本实现不需要变动,就换个导包即可
public class DinerMenuIterator implements Iterator<MenuItem> {
MenuItem[] items;
int pos = 0;
public DinerMenuIterator(MenuItem[] items) {
this.items = items;
}
public boolean hasNext() {
if (pos >= items.length || items[pos] == null) {
return false;
} else {
return true;
}
}
public MenuItem next() {
MenuItem item = items[pos];
pos++;
return item;
}
}
dinerMenu则也需要重新导入Iterator的包
public class DinerMenu {
static final int MAX_ITEMS = 6;
public int numberOfItems = 0;
MenuItem[] menuItems;
Iterator<MenuItem> iterator;
public DinerMenu() {
menuItems = new MenuItem[MAX_ITEMS];
addMenuItem("Diner Menu1", "xxxxx", true, 3.99);
addMenuItem("Diner with meat", "xxxxx", false, 4.99);
addMenuItem("Diner Menu2", "xxxxx", true, 4.49);
addMenuItem("Diner with pork", "xxxx", false, 5.99);
iterator = new DinerMenuIterator(menuItems);
}
public void addMenuItem(String name, String description,
boolean vegetarian, double price) {
MenuItem item = new MenuItem(name, description, vegetarian, price);
if (numberOfItems >= MAX_ITEMS) {
System.out.println("menu is full! Can't add item to menu any more");
} else {
menuItems[numberOfItems] = item;
numberOfItems++;
}
}
public Iterator<MenuItem> createIterator() {
return this.iterator;
}
}
测试类稍作整修
public static void main(String[] args) {
PancakeHouseMenu breakfastMenu = new PancakeHouseMenu();
Iterator<MenuItem> iterator1 = breakfastMenu.createIterator();
while (iterator1.hasNext()) {
System.out.println(iterator1.next());
}
DinerMenu dinerMenu = new DinerMenu();
Iterator<MenuItem> iterator2 = dinerMenu.createIterator();
while (iterator2.hasNext()) {
System.out.println(iterator2.next());
}
}
输出结果仍然不变:
name =Pancake Breakfast1 description xxxxx isVegetarian true price= 2.99
name =Pancake Breakfast with meat description xxxxx isVegetarian false price= 3.99
name =Pancake Breakfast description xxxxx isVegetarian true price= 3.49
name =Pancake Breakfast with pork description xxxx isVegetarian false price= 4.99
name =Diner Menu1 description xxxxx isVegetarian true price= 3.99
name =Diner with meat description xxxxx isVegetarian false price= 4.99
name =Diner Menu2 description xxxxx isVegetarian true price= 4.49
name =Diner with pork description xxxx isVegetarian false price= 5.99
但是类的结构图却已经变化:
注意,此处类图与书中不同(主要是没有实现Menu接口以及女招待对象),属个人理解。如有错误,请指出
迭代器模式的作用
- 迭代器的出现,让我们无需关心相关model的实现结构
- 将数据的遍历抽离出来,使得单一职责原则能够很好的实现。
迭代器的替换
事实上,JDK 1.5新增了增强的for循环,让我们对于Collections的集合类,遍历更加方便,对于任意Collections,我们都可以这样遍历:
for(Object m :items){
System.out.println("xxxx");
}
这样即使不使用Iterator,我们仍然可以遍历元素
迭代器的多次使用
之前,我的实现与书中不同,以为实现Menu接口并无甚用,后面才发现,是为了遍历Menu而做的铺垫。书中最后的迭代器实例,先对存放Menu集合的list进行遍历,之后,对每个menu内的Item项进行遍历。因此,有两次Iterator的使用。现实现如下:(需对上述代码略作修改)
增加Menu接口
public interface Menu {
public Iterator<MenuItem> createIterator();
}
让DinerMenu PancakeHouseMenu都实现Menu接口;因为之前是直接写了createIterator方法,因此这次只需在后面加上 implements Menu即可
添加Waitress类的实现
public class Waitress {
ArrayList<Menu> menus;
public Waitress(Menu dinerMenu,Menu pancakeHouseMenu) {
menus = new ArrayList<Menu>();
menus.add(dinerMenu);
menus.add(pancakeHouseMenu);
}
public void printMenu(){
Iterator<Menu> menusIterator = menus.iterator();
while(menusIterator.hasNext()){
printMenu(menusIterator.next().createIterator());
}
// for(Menu menu : menus){
// printMenu(menu.createIterator());
// }
}
private void printMenu(Iterator<MenuItem> iterator) {
while (iterator.hasNext()) {
System.out.println(iterator.next());
}
}
}
修改测试类
public class Printer2 {
public static void main(String[] args) {
PancakeHouseMenu breakfastMenu = new PancakeHouseMenu();
DinerMenu dinerMenu = new DinerMenu();
Waitress w = new Waitress(dinerMenu, breakfastMenu);
w.printMenu();
}
}
输出不变
name =Diner Menu1 description xxxxx isVegetarian true price= 3.99
name =Diner with meat description xxxxx isVegetarian false price= 4.99
name =Diner Menu2 description xxxxx isVegetarian true price= 4.49
name =Diner with pork description xxxx isVegetarian false price= 5.99
name =Pancake Breakfast1 description xxxxx isVegetarian true price= 2.99
name =Pancake Breakfast with meat description xxxxx isVegetarian false price= 3.99
name =Pancake Breakfast description xxxxx isVegetarian true price= 3.49
name =Pancake Breakfast with pork description xxxx isVegetarian false price= 4.99