Help us understand the problem. What is going on with this article?

JavaでIteratorパターン

More than 1 year has passed since last update.

はじめに

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パターンに関して学びました。
以下でサンプルコードをアップしていますのでよろしければ参考にどうぞ。

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

参考文献

Why not register and get more from Qiita?
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
Comments
No comments
Sign up for free and join this conversation.
If you already have a Qiita account
Why do not you register as a user and use Qiita more conveniently?
You need to log in to use this function. Qiita can be used more conveniently after logging in.
You seem to be reading articles frequently this month. Qiita can be used more conveniently after logging in.
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
ユーザーは見つかりませんでした