Java 设计模式——迭代器模式

目录

1.概述

迭代器模式 (Iterator Pattern) 是一种行为型设计模式,它提供一种顺序访问聚合对象(如列表、集合等)中的元素,而无需暴露聚合对象的内部表示 。迭代器模式将遍历逻辑封装在一个迭代器对象中,使得我们可以使用统一的方式遍历不同类型的聚合对象,同时也可以简化客户端代码。

2.结构

迭代器模式主要包含以下角色:

  • 抽象聚合 (Aggregate) 角色:定义存储、添加、删除聚合元素以及创建迭代器对象的接口。
  • 具体聚合 (ConcreteAggregate) 角色:实现抽象聚合类,返回一个具体迭代器的实例。
  • 抽象迭代器 (Iterator) 角色 :定义访问和遍历聚合元素的接口,通常包含 hasNext()next() 等方法。
  • 具体迭代器 (Concretelterator) 角色:实现抽象迭代器接口中所定义的方法,完成对聚合对象的遍历,记录遍历的当前位置。

3.案例实现

【例】定义一个可以存储学生对象的容器对象,将遍历该容器的功能交由迭代器实现,其类图如下:

具体实现代码如下:
Student.java

java 复制代码
public class Student {
    private String name;
    private String number;
    
    public Student() {
    }
    
    public Student(String name, String number) {
        this.name = name;
        this.number = number;
    }
    
    @Override
    public String toString() {
        return "Student{" +
                "name='" + name + '\'' +
                ", number='" + number + '\'' +
                '}';
    }
    
    public String getName() {
        return name;
    }
    
    public void setName(String name) {
        this.name = name;
    }
    
    public String getNumber() {
        return number;
    }
    
    public void setNumber(String number) {
        this.number = number;
    }
}

3.1.抽象迭代器

StudentIterator.java

java 复制代码
//抽象迭代器角色接口
public interface StudentIterator {
    //判断是否还有元素
    boolean hasNext();
    
    //获取下一个元素
    Student next();
}

3.2.具体迭代器

StudentIteratorImpl.java

java 复制代码
//具体迭代器角色类
public class StudentIteratorImpl implements StudentIterator{
    
    private List<Student> list;
    //用来记录遍历时的位置
    private int position = 0;
    
    public StudentIteratorImpl(List<Student> list) {
        this.list = list;
    }
    
    @Override
    public boolean hasNext() {
        return position < list.size();
    }
    
    @Override
    public Student next() {
        //从集合中或者去指定位置的元素
        Student currentStudent = list.get(position);
        position++;
        return currentStudent;
    }
}

3.3.抽象聚合

StudentAggregate.java

java 复制代码
//抽象聚合(容器)角色接口
public interface StudentAggregate {
    //添加学生
    void addStudent(Student stu);
    
    //删除学生
    void removeStudent(Student stu);
    
    //获取迭代器对象
    StudentIterator getStudentIterator();
}

3.4.具体聚合

StudentAggregateImpl.java

java 复制代码
public class StudentAggregateImpl implements StudentAggregate{
    
    private List<Student> list = new ArrayList<>();
    
    @Override
    public void addStudent(Student stu) {
        list.add(stu);
    }
    
    @Override
    public void removeStudent(Student stu) {
        list.remove(stu);
    }
    
    //获取迭代器对象
    @Override
    public StudentIterator getStudentIterator() {
        return new StudentIteratorImpl(list);
    }
}

3.5.测试

Client.java

java 复制代码
public class Client {
    public static void main(String[] args) {
        //创建聚合(容器)对象
        StudentAggregateImpl aggregate = new StudentAggregateImpl();
        Student student1 = new Student("Tom", "1001");
        Student student2 = new Student("Mike", "1002");
        Student student3 = new Student("Jerry", "1003");
        Student student4 = new Student("Mary", "1004");
        //添加元素
        aggregate.addStudent(student1);
        aggregate.addStudent(student2);
        aggregate.addStudent(student3);
        aggregate.addStudent(student4);
        
        //删除元素
        aggregate.removeStudent(student3);
        
        //遍历聚合对象
        // 1.获取迭代器对象
        StudentIterator iterator = aggregate.getStudentIterator();
        // 2.遍历
        while (iterator.hasNext()){
            // 3.获取元素
            Student student = iterator.next();
            System.out.println(student.toString());
        }
    }
}

结果如下:

java 复制代码
Student{name='Tom', number='1001'}
Student{name='Mike', number='1002'}
Student{name='Mary', number='1004'}

4.优缺点

(1)迭代器模式的优缺点如下:

  • 优点
    • 简化客户端代码:迭代器模式将遍历逻辑封装在迭代器对象中,客户端代码只需通过迭代器接口与迭代器对象交互,无需关心具体的遍历算法和聚合对象的内部结构,从而简化了客户端代码。
    • 统一遍历接口:迭代器模式提供一种统一的遍历接口,使得对不同类型的聚合对象可以使用相同的遍历方式,提供了更加灵活和通用的遍历操作。
    • 支持多种遍历方式:通过不同的迭代器实现,迭代器模式支持多种遍历方式,如顺序遍历、逆序遍历等,使得遍历操作更加灵活可变。
    • 解耦聚合对象与遍历操作:迭代器模式将遍历操作从聚合对象中分离出来,使得聚合对象的内部结构不再暴露给客户端,遵循了单一职责原则,提高了代码的可维护性和可扩展性。
  • 缺点
    • 对于某些较为复杂的聚合对象,实现迭代器可能会比较复杂,需要实现多个方法,并考虑并发访问的线程安全性,增加了代码的复杂性。
    • 当遍历的聚合对象发生变化时,需要相应地修改迭代器的实现,可能会影响到迭代器的逻辑。

(2)总体来说,迭代器模式适用于需要统一遍历接口、封装遍历逻辑、解耦聚合对象与遍历操作的场景。它可以提供更加灵活、通用和可维护的遍历操作,但在某些情况下,可能会增加代码的复杂性。因此,在使用迭代器模式时需要综合考虑具体的设计需求和项目情况。

5.使用场景

(1)迭代器模式通常适用于以下场景:

  • 需要遍历聚合对象的元素:当需要遍历并访问聚合对象中的元素,并且不希望暴露聚合对象的内部结构给客户端时,可以使用迭代器模式。它可以将遍历操作封装在迭代器中,提供一个统一的遍历接口。
  • 需要对不同类型的聚合对象使用统一的遍历方式:迭代器模式提供了一种统一的遍历接口,使得对不同类型的聚合对象可以使用相同的遍历方式。这样可以增加代码的灵活性和可维护性,使得客户端代码更加通用。
  • 需要支持多种遍历方式:如果需要支持多种遍历方式,如顺序遍历、逆序遍历等,可以通过不同的迭代器实现来实现不同的遍历方式。这样可以使遍历操作更加灵活可变。
  • 需要屏蔽聚合对象的内部实现细节:当聚合对象的内部结构较为复杂,或者不希望暴露内部结构给客户端时,可以使用迭代器模式进行封装。迭代器模式将遍历逻辑封装在迭代器对象中,客户端无需关心聚合对象的具体实现细节。
  • 需要遍历操作与聚合对象解耦:迭代器模式将遍历操作从聚合对象中分离出来,使得聚合对象的内部结构不再暴露给客户端。这样可以提高代码的可维护性和可扩展性,使遍历操作与聚合对象解耦。

(2)迭代器模式在现实生活中的应用也非常广泛,例如集合类、数据库查询结果等都可以使用迭代器模式来统一遍历操作接口。

6.JDK 源码解析------Iterator

(1)迭代器模式在 Java 的很多集合类中被广泛应用,接下来看下面的例子:

java 复制代码
public class TestArratList {
    public static void main(String[] args) {
        List<String> list = new ArrayList<>();
        //添加元素
        list.add("1001");
        list.add("1002");
        list.add("1003");
        //删除元素
        list.remove("1003");
        Iterator<String> iterator = list.iterator();
        while (iterator.hasNext()){
            System.out.println(iterator.next());
        }
    }
}

结果如下:

java 复制代码
1001
1002

(2)看完这段代码是不是觉得很熟悉,它与我们上面的代码基本类似。单列集合都使用到了迭代器,所以现在以 ArrayList 举例来说明:

  • List:抽象聚合类;
  • ArrayList:具体的聚合类;
  • Iterator:抽象迭代器;
  • list.iterator():返回的是实现了 Iterator 接口的具体迭代器对象;

(3)具体来看看 ArrayList 的部分关键代码实现:

java 复制代码
public class ArrayList<E> extends AbstractList<E>
        implements List<E>, RandomAccess, Cloneable, java.io.Serializable {
    
    public Iterator<E> iterator() {
        return new Itr();
    }
    
    private class Itr implements Iterator<E> {
        int cursor;       	// 下一个要返回元素的索引
        int lastRet = -1; 	// 上一个返回元素的索引
        int expectedModCount = modCount;

        Itr() {}
		
        //判断是否还有元素
        public boolean hasNext() {
            return cursor != size;
        }

        //获取下一个元素
        public E next() {
            checkForComodification();
            int i = cursor;
            if (i >= size)
                throw new NoSuchElementException();
            Object[] elementData = ArrayList.this.elementData;
            if (i >= elementData.length)
                throw new ConcurrentModificationException();
            cursor = i + 1;
            return (E) elementData[lastRet = i];
        }
        
        //...
}

(4)这部分代码大致就是在 iterator 方法中返回了一个实例化的 Iterator 对象。Itr 是一个内部类,它实现了 Iterator 接口并重写了其中的抽象方法。此外,通过下图可知 ArrayList 与 Iterable 的关系:

java 复制代码
package java.lang;

import java.util.Iterator;
import java.util.Objects;
import java.util.Spliterator;
import java.util.Spliterators;
import java.util.function.Consumer;

public interface Iterable<T> {
    Iterator<T> iterator();

    default void forEach(Consumer<? super T> action) {
        Objects.requireNonNull(action);
        for (T t : this) {
            action.accept(t);
        }
    }

    default Spliterator<T> spliterator() {
        return Spliterators.spliteratorUnknownSize(iterator(), 0);
    }
}

(5)当我们在使用 Java 开发的时想使用迭代器模式的话,只要让我们自己定义的容器类实现 java.util.Iterable 并实现其中的 iterator() 方法使其返回一个 java.util.Iterator 的实现类就可以了。

相关推荐
serve the people14 分钟前
python环境搭建 (六) Makefile 简单使用方法
java·服务器·python
重生之后端学习17 分钟前
146. LRU 缓存
java·数据结构·算法·leetcode·职场和发展
萧曵 丶19 分钟前
懒加载单例模式中DCL方式和原理解析
java·开发语言·单例模式·dcl
回忆是昨天里的海22 分钟前
k8s部署的微服务动态扩容
java·运维·kubernetes
萧曵 丶23 分钟前
单例模式 7 种实现方式对比表
java·单例模式
lang2015092833 分钟前
Tomcat Maven插件全解析:开发部署一体化
java·tomcat·maven
JHC_binge40 分钟前
国内Ubuntu 22.04 LTS安装Milvus向量数据库
java·linux·ubuntu
2501_941148151 小时前
C++ map / multimap 保姆级教程
java·开发语言·c++
Yield & Allure1 小时前
EasyExcel使用
java
符哥20081 小时前
Fastjson2.X 使用详解
android·java