1. 迭代器模式概述

1.1 什么是迭代器模式?

迭代器模式(Iterator Pattern)是一种行为设计模式,它用于提供一种方法顺序访问集合对象中的各个元素,而无需暴露其内部结构。其核心思想是通过迭代器对象来遍历集合,使得客户端代码不必关注集合的具体实现方式。

1.2 迭代器模式的优点

  • 封装性:隐藏集合的内部表示,仅通过迭代器提供访问方式。

  • 一致性:统一不同集合的访问方式,提高代码的可读性和可维护性。

  • 支持多种遍历方式:可以实现正序、倒序、懒加载等不同遍历方式。

2. 在 Java 文件读取中的应用

在实际应用中,文件读取通常需要处理大量数据,采用迭代器模式可以有效优化内存使用,提高性能。本文展示了两种基于迭代器模式的文件读取方式:一次性加载懒加载

2.1 一次性加载(UserFile

package com.xxx;

import java.io.File;
import java.io.IOException;
import java.nio.file.Files;
import java.util.Iterator;
import java.util.List;
import java.util.NoSuchElementException;
import java.util.stream.Collectors;

public class UserFile implements Iterable<User> {
    private final File file;

    public UserFile(File file) {
        this.file = file;
    }

    @Override
    public Iterator<User> iterator() {
        return new UserIterator();
    }

    class UserIterator implements Iterator<User> {
        List<User> userList = loadUsersFromFile();
        int cursor = 0;

        private List<User> loadUsersFromFile() {
            try {
                long beforeMemory = usedMemory();
                List<User> users = Files.readAllLines(file.toPath()).stream().map(line -> {
                    String substring = line.substring(1, line.length() - 1);
                    String[] split = substring.split(",");
                    return new User(split[0], Integer.parseInt(split[1]));
                }).collect(Collectors.toList());
                long afterMemory = usedMemory();  // 记录加载后内存
                System.out.println("一次性加载内存占用:" + (afterMemory - beforeMemory) / (1024 * 1024) + " MB");
                return users;
            } catch (IOException e) {
                e.printStackTrace();
                return null;
            }
        }

        @Override
        public boolean hasNext() {
            return cursor != userList.size();
        }

        @Override
        public User next() {
            if(cursor >= userList.size()) {
                 throw new NoSuchElementException();
            }
            int cursorIndex = cursor;
            cursor++;
            return userList.get(cursorIndex);
        }
    }

    private long usedMemory() {
        Runtime runtime = Runtime.getRuntime();
        return runtime.totalMemory() - runtime.freeMemory();  // 计算当前堆内存使用情况
    }
}

特点

  • 一次性读取整个文件并存入 List<User>,然后通过 Iterator 顺序访问。

  • 适用于小文件,因为所有数据都会存入内存。

  • 加载时占用较大内存,但遍历速度快。


2.2 懒加载(LazyUserFile

package com.xxx;

import java.io.BufferedReader;
import java.io.File;
import java.io.IOException;
import java.nio.file.Files;
import java.util.Iterator;
import java.util.NoSuchElementException;

public class LazyUserFile implements Iterable<User>{
    private final File file;

    public LazyUserFile(File file) {
        this.file = file;
    }

    @Override
    public Iterator<User> iterator() {
        return new LazyUserIterator();
    }

    class LazyUserIterator implements Iterator<User>{
        private BufferedReader reader;
        private String nextLine;
        private long beforeMemory;
        public LazyUserIterator() {
            try {
                beforeMemory = usedMemory();
                reader = Files.newBufferedReader(file.toPath());
                nextLine = reader.readLine();
            } catch (IOException e) {
                e.printStackTrace();
                nextLine = null;
            }
        }

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

        @Override
        public User next() {
            if (nextLine == null) {
                throw new NoSuchElementException();
            }
            String line = nextLine;
            nextLine = readNextLine();// 读取下一行

            if(nextLine == null) {
                long afterMemory = usedMemory();
                System.out.println("懒加载内存占用:" + (afterMemory - beforeMemory) / (1024 * 1024) + " MB");
                try {
                    reader.close();
                } catch (IOException e) {
                    throw new RuntimeException(e);
                }
            }
            return parseUser(line);
        }


        private String readNextLine() {
            try {
                return reader.readLine();
            } catch (IOException e) {
                e.printStackTrace();
                return null;
            }
        }


        private User parseUser(String line) {
            String substring = line.substring(1, line.length() - 1);
            String[] split = substring.split(",");
            return new User(split[0], Integer.parseInt(split[1]));
        }
    }

    private long usedMemory() {
        Runtime runtime = Runtime.getRuntime();
        return runtime.totalMemory() - runtime.freeMemory();  // 计算当前堆内存使用情况
    }
}

特点

  • 逐行读取文件,每次只在内存中存储一个 User

  • 适用于大文件,大幅减少内存占用。

  • 遍历速度稍慢,因为每次都要从文件读取数据。

3. 两种方式的对比

方式

内存占用

适用场景

读取速度

GC 影响

一次性加载

小文件

GC 压力大

懒加载

大文件

GC 影响小

4. 总结

  • 迭代器模式为遍历集合提供了一种 统一的方式,使代码更加解耦

  • 一次性加载(UserFile 适合小文件,懒加载(LazyUserFile 适合大文件。

  • 合理使用迭代器模式,可以优化 Java 应用的内存占用,提升程序性能。