迭代器模式

参考:【设计模式实战】迭代器模式

1.理解增强for循环的本质

java 复制代码
    public static void main(String[] args) {

        List<User> userList = new ArrayList<>();
        userList.add(new User("Tom", 12));
        userList.add(new User("Bob", 13));

        for(User u: userList){
            System.out.println(u);
        }
    }

为什么userList可以可以使用增强for循环,而我们自定义的User对象不能使用增强for 循环。要理解其中发生了什么,我们可以查看编译后的字节码:

发现其实增强for循环等价于以下写法:

java 复制代码
    public static void main(String[] args) {

        List<User> userList = new ArrayList<>();
        userList.add(new User("Tom", 12));
        userList.add(new User("Bob", 13));


        Iterator<User> iterator = userList.iterator();
        while (iterator.hasNext()){
            User u = iterator.next();
            System.out.println(u);
        }
    }

发现增强for循环的背后是通过生成iterator对象,从而利用iterator提供的方法进行遍历。

2. java中的Iterator类

接下来我们可以查看List背后的源码,观察其是如何实现迭代器模式的。


一层层点击,发现实质上List实现了Iterable的接口并重写了iterator()才具备迭代功能。

因此如果我们想要使用增强for循环,就必须使遍历的对象实现Iterable的接口。

3. 探索ArrayList背后的迭代器模式

ArrayList实现了Iterable(可迭代对象)的接口并重写了iterator(),返回一个迭代器(Iterator)对象

其中迭代器对象的hasNext主要根据当前的游标和数组的尺寸大小进行判断。next()首先进行了一系列判断,然后获取当前游标的值并使得游标加一。

那我们是否能在迭代的过程中增加或删除数据呢?通过以下代码进行验证

java 复制代码
    public static void main(String[] args) {

        List<User> userList = new ArrayList<>();
        userList.add(new User("Tom", 12));
        userList.add(new User("Bob", 13));


        Iterator<User> iterator = userList.iterator();
        while (iterator.hasNext()){
            User u = iterator.next();
            if(u.getAge()==12){
                userList.add(new User("txt",13));
            }
        }
    }

发现抛出异常,点进去发现原来是在 checkForComodification();抛出异常。

因此,在 Java 中,直接通过 for-each 循环(或普通 for 循环结合 Iterator)遍历集合时,直接调用 List 的 add()、remove() 等方法修改集合会触发 ConcurrentModificationException。这是因为集合的 modCount(修改计数器)和迭代器的 expectedModCount(预期修改计数器)不一致,导致迭代器的并发修改检查失败。正确的做法是:显式使用 Iterator 的 remove() 方法,通过Iterator遍历并调用其自身(iterator)的 remove() 方法删除元素,此时 Iterator 会同步更新 expectedModCount,避免异常。

4. 案例:读取文件,避免OM问题

改造以下原始代码,利用迭代器模式,读取问题,避免内存溢出问题。

java 复制代码
    public static void main(String[] args) throws IOException {
        List<User> userList=new ArrayList<>();
        String filePath = FileRead.class.getClassLoader().getResource("User.user").getPath();
        List<String> lines = Files.readAllLines(new File(filePath).toPath());
        for(String  l: lines){
            String substring = l.substring(1, l.length() - 1);
            String[] split = substring.split(",");
            userList.add(new User(split[0],Integer.parseInt(split[1])));
        }
    }

利用 BufferedReader 的迭代器模式逐行读取文件内容,并避免内存溢出问题

java 复制代码
public class ReadFile {

    public static void main(String[] args) throws IOException {
        String filePath = ReadFile.class.getClassLoader().getResource("User.user").getPath();

        // 使用 BufferedReader 逐行读取文件内容
        try (BufferedReader reader = Files.newBufferedReader(Paths.get(filePath))) {
            Iterator<String> lineIterator = new LineIterator(reader);

            while (lineIterator.hasNext()) {
                String line = lineIterator.next();
                // 去掉首尾的括号并分割字段
                String substring = line.substring(1, line.length() - 1);
                String[] split = substring.split(",");

                // 创建 User 对象并处理(此处仅打印作为示例)
                User user = new User(split[0], Integer.parseInt(split[1]));
                System.out.println(user);
            }
        }
    }

    // 自定义 LineIterator 实现迭代器模式
    static class LineIterator implements Iterator<String> {
        private final BufferedReader reader;
        private String nextLine;

        public LineIterator(BufferedReader reader) throws IOException {
            this.reader = reader;
            this.nextLine = reader.readLine(); // 初始化第一行
        }

        @Override
        public boolean hasNext() {
            return nextLine != null;
        }

        @Override
        public String next() {
            String currentLine = nextLine;
            try {
                nextLine = reader.readLine(); // 读取下一行
            } catch (IOException e) {
                throw new RuntimeException("Error reading next line", e);
            }
            return currentLine;
        }
    }

    // User 类定义
    static class User {
        private final String name;
        private final int age;

        public User(String name, int age) {
            this.name = name;
            this.age = age;
        }

        @Override
        public String toString() {
            return "User{name='" + name + "', age=" + age + "}";
        }
    }
}
相关推荐
方也_arkling21 小时前
【Java-Day08】static / final / 枚举
java·开发语言
橙淮21 小时前
Spring Bean作用域与生命周期全解析
java·spring
Chengbei1121 小时前
一站式源码安全检测工具、云安全 / APP / 小程序源码敏感信息递归多层目录扫描AK、JWT、手机号、身份证等敏感信息
java·开发语言·安全·web安全·网络安全·系统安全·安全架构
llz_11221 小时前
web-第一次课后作业
java·开发语言·idea
秋921 小时前
Java项目运行5天左右自动宕机:系统性定位与解决方案
java·开发语言·python
小江的记录本21 小时前
【JVM虚拟机】垃圾回收GC:垃圾收集器:CMS:核心原理、回收流程、优缺点、废弃原因(附《思维导图》+《面试高频考点清单》)
java·jvm·后端·python·spring·面试·maven
DIY源码阁1 天前
JavaSwing学生成绩管理系统 - MySQL版
java·数据库·mysql·eclipse
basketball6161 天前
C++ NULL 和 nullptr 区别 以及 nullptr 的核心实现
java·开发语言·c++
JAVA面经实录9171 天前
MyBatis面试题库
java·mybatis
小江的记录本1 天前
【JVM虚拟机】垃圾回收GC:垃圾回收算法:标记-清除、标记-复制、标记-整理、分代收集(附《思维导图》+《面试高频考点清单》)
java·jvm·后端·python·算法·安全·面试