【迭代器模式】

42 阅读4分钟

定义

迭代器模式(Iterator Pattern)是一种行为型设计模式,它提供一种方法顺序访问一个聚合对象中的各个元素,而又不暴露该对象的内部表示

以下是迭代器模式的主要特点和定义:

主要角色

  1. Iterator(迭代器接口)

    • 定义了访问和遍历聚合对象元素的接口,通常包括方法如hasNext(判断是否还有下一个元素)、next(返回下一个元素)和remove(删除当前元素)等。
  2. ConcreteIterator(具体迭代器)

    • 实现了迭代器接口,对特定的聚合对象进行遍历。
    • 跟踪聚合对象中的当前位置,并能够根据迭代器接口的定义实现遍历操作。
  3. Aggregate(聚合接口)

    • 定义了创建迭代器的接口,通常只有一个方法createIterator用于创建一个迭代器对象。
  4. ConcreteAggregate(具体聚合)

    • 实现了聚合接口,创建并返回一个具体的迭代器对象,该迭代器用于遍历此聚合对象中的元素。

业务

在 Java 中,迭代器模式有很多常见的使用场景。

一、集合框架中的迭代器

Java 的集合框架(如ListSetMap等)广泛使用了迭代器模式。以ArrayList为例:

import java.util.ArrayList;
import java.util.Iterator;

public class JavaIteratorExample {
    public static void main(String[] args) {
        ArrayList<String> list = new ArrayList<>();
        list.add("Apple");
        list.add("Banana");
        list.add("Cherry");

        // 使用迭代器遍历列表
        Iterator<String> iterator = list.iterator();
        while (iterator.hasNext()) {
            String item = iterator.next();
            System.out.println(item);
        }
    }
}

在这个例子中,ArrayList实现了Iterable接口,该接口定义了一个iterator方法用于创建一个迭代器对象。Iterator接口提供了hasNextnext等方法来遍历集合中的元素。

二、自定义数据结构的迭代器

假设我们有一个自定义的二叉树数据结构,我们可以为其实现一个迭代器来遍历树中的节点。

class BinaryTree {
    class Node {
        int value;
        Node left;
        Node right;

        Node(int value) {
            this.value = value;
        }
    }

    Node root;

    public BinaryTree() {
        root = null;
    }

    public void insert(int value) {
        root = insertRec(root, value);
    }

    private Node insertRec(Node root, int value) {
        if (root == null) {
            return new Node(value);
        }
        if (value < root.value) {
            root.left = insertRec(root.left, value);
        } else {
            root.right = insertRec(root.right, value);
        }
        return root;
    }

    public class InorderIterator implements Iterator<Node> {
        private java.util.Stack<Node> stack = new java.util.Stack<>();
        Node current;

        public InorderIterator() {
            current = root;
            pushLeft(current);
        }

        private void pushLeft(Node node) {
            while (node!= null) {
                stack.push(node);
                node = node.left;
            }
        }

        @Override
        public boolean hasNext() {
            return !stack.isEmpty();
        }

        @Override
        public Node next() {
            Node node = stack.pop();
            pushLeft(node.right);
            return node;
        }

        @Override
        public void remove() {
            throw new UnsupportedOperationException();
        }
    }
}

使用这个自定义二叉树的迭代器:

public class Main {
    public static void main(String[] args) {
        BinaryTree tree = new BinaryTree();
        tree.insert(5);
        tree.insert(3);
        tree.insert(7);
        tree.insert(2);
        tree.insert(4);
        tree.insert(6);
        tree.insert(8);

        BinaryTree.InorderIterator iterator = tree.new InorderIterator();
        while (iterator.hasNext()) {
            BinaryTree.Node node = iterator.next();
            System.out.println(node.value);
        }
    }
}

在这个例子中,我们为自定义的二叉树实现了一个中序遍历的迭代器。迭代器通过维护一个栈来实现遍历,在遍历过程中,它依次访问左子树、根节点和右子树。

通过这些例子可以看出,迭代器模式使得对各种数据结构的遍历更加方便和统一,同时隐藏了数据结构的内部实现细节。

总结

使用场景

  1. 访问一个聚合对象的内容而无需暴露它的内部表示。

    • 例如,一个链表、数组或树等数据结构,可以通过迭代器来遍历其元素,而外部代码不需要了解这些数据结构的内部实现细节。
  2. 支持对聚合对象的多种遍历方式。

    • 可以为同一个聚合对象创建不同的迭代器,实现不同的遍历策略,如正向遍历、反向遍历、深度优先遍历等。

优点

  1. 简化了聚合对象的遍历方式。

    • 客户端只需要使用迭代器提供的方法来遍历聚合对象,而不需要了解聚合对象的内部结构。
  2. 支持多种遍历方式。

    • 可以根据需要为聚合对象创建不同的迭代器,实现不同的遍历策略。
  3. 分离了聚合对象和遍历算法。

    • 使得聚合对象的实现和遍历算法的实现相互独立,便于代码的维护和扩展。

缺点

  1. 对于较为简单的聚合对象,使用迭代器模式可能会增加代码的复杂性。

    • 如果聚合对象本身的遍历操作很简单,直接在聚合对象中实现遍历方法可能更加高效。
  2. 迭代器的实现可能会比较复杂。

    • 特别是对于复杂的聚合对象,实现一个高效的迭代器可能需要考虑很多因素,如并发访问、性能优化等。