迭代器模式

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

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 + "}";
        }
    }
}
相关推荐
我谈山美,我说你媚4 分钟前
vue2自定义useVModel函数
java·前端·javascript
zhyhgx38 分钟前
【Spring】Spring配置文件
java·服务器·spring boot·后端·spring·配置文件
程序员JerrySUN40 分钟前
每天设计者模式-1:基础面试题
java·linux·运维·服务器·开发语言·python·docker
阿小木的愤怒1 小时前
Spring监听器Listener
java·spring·监听器·spring监听器
johnrui1 小时前
java8Optional 使用
java·开发语言
m0_748235952 小时前
SpringBoot:解决前后端请求跨域问题(详细教程)
java·spring boot·后端
LUCIAZZZ2 小时前
简单说一下什么是RPC
java·网络·网络协议·计算机网络·spring cloud·rpc
嘵奇2 小时前
最新版IDEA下载安装教程
java·intellij-idea
s_fox_2 小时前
Nginx Embedded Variables 嵌入式变量解析(4)
java·网络·nginx
Jelena157795857922 小时前
使用Java爬虫获取1688 item_get_company 接口的公司档案信息
java·开发语言·爬虫