Java
初心者
Iterator
デザインパターン
GoF


はじめに

GoFのデザインパターンを紹介している『増補改訂版 Java言語で学ぶデザインパターン入門』を読んで、学んだ内容についてまとめます。


Iteratorパターン


Iteratorとは

Iteratorとは英単語で日本語では「反復子」を意味します。

特にプログラミングにおいては、集合体に対して順次にアクセスを行う際に、「指し示すもの」を抽象化し、一般化したものを意味します。

この説明だけではイメージが掴みにくいと思うので、もう少し具体的な例をあげます。

以下のような配列arrayの要素を全て表示するような繰り返し文を書いたことがある人は多いのではないでしょうか。


sample.java

for (int i = 0; i < array.length; i++) {

System.out.println(array[i]);
}

ここで変数iは配列arrayの添え字となり、配列の持つ要素に順次にアクセスを行っています。

このように「繰り返して全体を指し示し、走査を行う」ものがIteratorです。


登場人物

Itaratorパターンで使用するのは以下のクラス図に登場するクラスとインターフェースです。

image.png


インターフェース



  • Aggreate

    数え上げを行う対象の「集合体」を表します。

    メソッドとしてはIterator()のみを持ちます。

    これはIterator生成するために使用します。


Aggregate.java

public interface Aggregate {

public abstract Iterator iterator();
}



  • Iterator

    集合の要素の「数え上げを行うもの」を表します。

    メソッドとしてはhasNext()next()の2つを持ちます。

    hasNext()は次の要素を持つかどうかを確認するために使用します。

    next()は次の要素があった際に次の要素を取得するために使用します。



Iterator.java

public interface Iterator {

public abstract boolean hasNext();

public abstract Object next();
}



実装クラス

インターフェースを実装し、具体的な処理を記述します。

具体例については後述いたします。


  • ConcreateAggreate

  • ConcreateIterator


具体例

具体例として、「従業員をまとめる従業員リスト」と「従業員リストのIterator」をもとに説明します。


実装クラス



  • Employeeクラス

    Employee(従業員)クラスは集合体の要素を表します。

    名前と従業員コードを持ちます。


Employee.java

public class Employee {

private String name;
private String employeeCode;

public Employee(String name, String employeeCode) {
this.name = name;
this.employeeCode = employeeCode;
}

public String getName() {
return name;
}

public String getEmployeeCode() {
return employeeCode;
}
}




  • EmployeeListクラス

    EmployeeList(従業員リスト)クラスは従業員の集合体を表します。

    従業員の配列と添え字を持ちます。

    iterator()において自身を引数としてEmployeeListIteratorを生成して返しています。


EmployeeList.java

public class EmployeeList implements Aggregate {

private Employee[] employees;
private int last = 0;

public EmployeeList(int maxsize) {
this.employees = new Employee[maxsize];
}

public Employee getEmployeeAt(int index) {
return employees[index];
}

public void appendEmployee(Employee employee) {
this.employees[last] = employee;
last++;
}

public int getLength() {
return last;
}

public Iterator iterator() {
return new EmployeeListIterator(this);
}
}




  • EmployeeListIteratorクラス

    EmployeeListIterator(従業員リストIterator)クラスは従業員の集合体を数え上げるものを表します。

    従業員リストと添え字を持ちます。

    hasNext()で従業員リストが次の要素を持つかを確認し、next()で従業員リストの次の要素(従業員)を返却しています。
    順次にアクセスを行う方法は様々ですが、今回は単純に前から順次でアクセスする実装を行います。


EmployeeListIterator.java

public class EmployeeListIterator implements Iterator {

private EmployeeList employeeList;
private int index;

public EmployeeListIterator(EmployeeList employeeList) {
this.employeeList = employeeList;
this.index = 0;
}

public boolean hasNext() {
if (index < employeeList.getLength()) {
return true;
} else {
return false;
}
}

public Object next() {
Employee employee = employeeList.getEmployeeAt(index);
index++;
return employee;
}
}



実行クラス



  • Mainクラス

    従業員リストに従業員を追加し、追加した従業員の名前と従業員コードを従業員リストから出力しています。


Main.java

public class Main {

public static void main(String[] args) {
EmployeeList employeeList = new EmployeeList(4);
employeeList.appendEmployee(new Employee("Tarou_Tanaka", "C001"));
employeeList.appendEmployee(new Employee("Hanako_Yamada", "C002"));
employeeList.appendEmployee(new Employee("Yuuya_Suzuki", "C003"));
employeeList.appendEmployee(new Employee("Kanako_Satou", "C004"));
Iterator it = employeeList.iterator();
while (it.hasNext()) {
Employee employee = (Employee) it.next();
System.out.println(employee.getName() + ":" + employee.getEmployeeCode());
}
}
}


実行結果

Main.classを実行した結果は以下になります。

employeeListに追加した順にきちんと出力されていることが確認できます。


実行結果

Tarou_Tanaka:C001

Hanako_Yamada:C002
Yuuya_Suzuki:C003
Kanako_Satou:C004


メリット

Iteratorを使用することで数え上げを行う際に集合体がどのような集合体であるか(配列、ArrayList、Vectorなど)、どのような実装が行われているかを意識する必要がなくなります。

以下で集合体を配列からArrayListに変更したEmployeeList.classを定義します。

この時、実行クラスのMain.classを修正する必要はなく、実行結果は配列の場合と同様です。


EmployeeList.java

import java.util.ArrayList;

public class EmployeeList implements Aggregate {
private ArrayList<Employee> employees;

public EmployeeList(int initialsize) {
this.employees = new ArrayList<>(initialsize);
}

public Employee getEmployeeAt(int index) {
return (Employee) employees.get(index);
}

public void appendEmployee(Employee employee) {
employees.add(employee);
}

public int getLength() {
return employees.size();
}

public Iterator iterator() {
return new EmployeeListIterator(this);
}
}



実行結果

Tarou_Tanaka:C001

Hanako_Yamada:C002
Yuuya_Suzuki:C003
Kanako_Satou:C004


追記 (2018/10/31)

Iteratorには前から順次でアクセスする以外にも様々なアクセス方法があります。(逆順、1つ飛ばしなど)

今回はArrayListで逆順でアクセスするIteratorを追加いたしました。

差分のあるクラスのみ掲載しますので、その他のクラスについてはまとめで確認してください。


インタフェース



  • Aggregate

    逆順にアクセスするIteratorを生成するreverseIterator()を追加しています。


Aggregate.class

public interface Aggregate {

public abstract Iterator iterator();

public abstract Iterator reverseIterator();
}



実装クラス



  • EmployeeListクラス

    reverseIterator()を追加しています。

    reverseIterator()において自身を引数としてEmployeeListReverseIteratorを生成して返しています。


EmployeeList.class

import java.util.ArrayList;

public class EmployeeList implements Aggregate {
private ArrayList<Employee> employees;

public EmployeeList(int initialsize) {
this.employees = new ArrayList<>(initialsize);
}

public Employee getEmployeeAt(int index) {
return (Employee) employees.get(index);
}

public void appendEmployee(Employee employee) {
employees.add(employee);
}

public int getLength() {
return employees.size();
}

public Iterator iterator() {
return new EmployeeListIterator(this);
}

public Iterator reverseIterator() {
return new EmployeeListReverseIterator(this);
}
}




  • EmployeeListReverseIteratorクラス

    新規に作成するクラスですが、順次アクセスを行うEmployeeListIterator.classと同様にhasNext()next()を実装しています。

    コンストラクタでemployeeListの要素数から1を引いた数字をindexに持ちます。

    これにより仮に要素数が5のリストからは4を取得することになります。

    hasNext()ではArrayListの要素の数だけ逆順からアクセスを行います。

    注意すべき点としてはループの条件でindexが0未満にならないようにする必要があることです。

    この条件がなければnext()でemployeeList[-1]にアクセスしようとしてしまいArrayIndexOutOfBoundsExceptionが発生します。


EmployeeListReverseIterator.class

public class EmployeeListReverseIterator implements Iterator {

private EmployeeList employeeList;
private int index;

public EmployeeListReverseIterator(EmployeeList employeeList) {
this.employeeList = employeeList;
this.index = employeeList.getLength() - 1;
}

public boolean hasNext() {
if (index < employeeList.getLength() && index >= 0) {
return true;
} else {
return false;
}
}

public Object next() {
Employee employee = employeeList.getEmployeeAt(index);
index--;
return employee;
}
}



実行クラス



  • Mainクラス

    EmployeeListEmployeeを追加し、順次で出力をした後、逆順で出力を行っています。


Main.class

public class Main {

public static void main(String[] args) {
EmployeeList employeeList = new EmployeeList(4);
employeeList.appendEmployee(new Employee("Tarou_Tanaka", "C001"));
employeeList.appendEmployee(new Employee("Hanako_Yamada", "C002"));
employeeList.appendEmployee(new Employee("Yuuya_Suzuki", "C003"));
employeeList.appendEmployee(new Employee("Kanako_Satou", "C004"));
Iterator it = employeeList.iterator();
System.out.println("------- Order -------");
while (it.hasNext()) {
Employee employee = (Employee) it.next();
System.out.println(employee.getName() + ":" + employee.getEmployeeCode());
}
System.out.println("------- Reverse Order -------");
Iterator rit = employeeList.reverseIterator();
while (rit.hasNext()) {
Employee employee = (Employee) rit.next();
System.out.println(employee.getName() + ":" + employee.getEmployeeCode());
}
}
}


実行結果

Main.classを実行した結果は以下になります。

employeeList追加した順と、追加したものと逆順で出力されていることが確認できます。


実行結果

------- Order -------

Tarou_Tanaka:C001
Hanako_Yamada:C002
Yuuya_Suzuki:C003
Kanako_Satou:C004
------- Reverse Order -------
Kanako_Satou:C004
Yuuya_Suzuki:C003
Hanako_Yamada:C002
Tarou_Tanaka:C001


まとめ

集合体に順次にアクセスを行うIteratorパターンに関して学びました。

以下でサンプルコードをアップしていますのでよろしければ参考にどうぞ。

また、他のデザインパターンに関しては以下でまとめていますので、こちらも参考にどうぞ。


参考文献