java实现python的yield关键字的功能-生成器

总结

Java 没有 Python 中 yield 这种 “一键生成器” 的原生语法,但通过以下方式可实现完全等效的 “惰性生成、按需取值” 效果:
1. 基础场景:实现 Iterator/Iterable 接口(最贴近生成器的底层逻辑);
2. 常规场景:Java 8+ Stream API(简洁、高效,推荐);
3. 高并发场景:Java 21+ 虚拟线程 + Stream/Flow;
4. 大文件场景:BufferedReader.lines()(惰性逐行读取,替代 Python yield 读文件)。

基础场景

/*
 * Copyright (c) [2026] [Allen]. All rights reserved.
 *
 * This software is the confidential and proprietary information of
 * [Allen] ("Confidential Information"). You shall not disclose
 * such Confidential Information and shall use it only in accordance with
 * the terms of the license agreement you entered into with [Allen].
 *
 * @author: [Allen]
 * @date: 2026-02-23
 * @version: 1.0
 * @description: 斐波那契数列生成器(模拟Python yield关键字的惰性迭代效果)
 *               实现类似Python yield的按需生成数据能力
 */
package com.allen.questions.generator;

import java.util.Iterator;

//

/**
 * 基于 Iterator/Iterable 手动实现(基础方案)-自定义斐波那契生成器(实现Iterable,支持for-each循环)
 * Java 中最贴近生成器的核心是迭代器模式:通过实现 Iterator 接口自定义 “按需生成数据” 的逻辑,搭配 Iterable 支持 for-each 循环,模拟 Python 生成器的 “暂停 / 恢复” 和 “惰性生成” 特性。
 */
public class FibonacciGenerator implements Iterable<Long> {
    private final int n; // 生成前n项

    public FibonacciGenerator(int n) {
        this.n = n;
    }

    // 核心:实现Iterator,定义按需生成逻辑
    @Override
    public Iterator<Long> iterator() {
        return new Iterator<Long>() {
            private long a = 0, b = 1; // 斐波那契初始值
            private int count = 0;      // 已生成的项数

            // 判断是否还有下一项
            @Override
            public boolean hasNext() {
                return count < n;
            }

            // 生成下一项(对应Python的yield)
            @Override
            public Long next() {
                if (!hasNext()) {
                    throw new java.util.NoSuchElementException();
                }
                long current = a; // 本次要返回的值(对应yield a)
                // 更新值(对应Python中yield后的a,b = b,a+b)
                long temp = a + b;
                a = b;
                b = temp;
                System.out.printf("a=%d, b=%d%n", a, b);
                count++;
                return current;
            }
        };
    }

    // 测试代码(对应Python的for循环)
    public static void main(String[] args) {
        FibonacciGenerator generator = new FibonacciGenerator(5);
        for (long num : generator) { // 触发迭代器的next()
            System.out.println(num);
        }
    }
}

 

 

常规场景

/*
 * Copyright (c) [2026] [Allen]. All rights reserved.
 *
 * This software is the confidential and proprietary information of
 * [Allen] ("Confidential Information"). You shall not disclose
 * such Confidential Information and shall use it only in accordance with
 * the terms of the license agreement you entered into with [Allen].
 *
 * @author: [Allen]
 * @date: 2026-02-23
 * @version: 1.0
 * @description: 使用java 8的流 api 实现python yield关键字的效果(懒性求值)
 */
package com.allen.questions.generator;

import java.util.stream.Stream;

/**
 *  Java 8+ Stream API(推荐,简洁版 “生成器”)即java 8 引入的 Stream 支持惰性求值,可以通过 Stream.generate() 或 IntStream.iterate() 实现生成器效果,尤其适合无限 / 大数据序列:
 * 核心特点:
 *      1. 惰性求值:Stream 不会一次性生成所有数据,只有调用 forEach()/collect() 时才会按需生成;
 *      2. 简洁高效:无需手动实现迭代器,适合快速开发;
 *      3. 支持并行处理:parallelStream() 可利用多核,Python 生成器不支持原生并行。
 */
public class FibonacciStream {
    public static void main(String[] args) {
        // 生成斐波那契数列(前5项),惰性求值
        Stream.iterate(
                        new long[]{0, 1}, // 初始值:[a, b]
                        arr -> new long[]{arr[1], arr[0] + arr[1]} // 迭代规则:更新a,b
                )
                .limit(5) // 限制生成5项(对应n=5)
                .map(arr -> arr[0]) // 取a值(对应yield a)
                .forEach(num -> {
                    System.out.println(num);
                    // 模拟Python中yield后的print(仅演示,实际可分离逻辑)
//                    long a = num == 0 ? 1 : (num == 1 ? 1 : (num == 1 ? 2 : 3));
//                    long b = num == 0 ? 1 : (num == 1 ? 2 : (num == 1 ? 3 : 5));
//                    if (num != 3) { // 最后一项不打印(和Python输出对齐)
//                        System.out.printf("a=%d, b=%d%n", a, b);
//                    }
                });
    }
}

 

高并发场景 && 大文件场景

根据业务需求可以使用并行流

/*
 * Copyright (c) [2026] [Allen]. All rights reserved.
 *
 * This software is the confidential and proprietary information of
 * [Allen] ("Confidential Information"). You shall not disclose
 * such Confidential Information and shall use it only in accordance with
 * the terms of the license agreement you entered into with [Allen].
 *
 * @author: [Allen]
 * @date: 2026-02-23
 * @version: 1.0
 * @description: 使用java 21的虚拟现场 api 实现python yield关键字的效果(懒性求值)
 */
package com.allen.questions.generator;

import java.io.BufferedReader;
import java.io.FileReader;
import java.io.IOException;
import java.nio.file.Paths;

/**
 * 使用java 21的虚拟线程 + java的stream
 * 逐行惰性读取,内存中始终只存当前行,和 Python yield 效果完全一致;
 * 资源安全:try-with-resources 自动关闭文件,对应 Python 的 with open()。
 */
public class LargeFileGenerator {
    // 模拟Python的yield读取大文件(返回Iterable,更贴近Python生成器语义)
    public static void readLargeFile(String filePath) {
        System.out.println("开始读取文件:" + filePath); // 打印路径,便于排查
        // 惰性逐行读取(核心:每行按需生成,对应Python yield)
        try (BufferedReader br = new BufferedReader(new FileReader(filePath))) {
            br.lines().parallel()
                    .map(String::strip) // 对应Python的strip()
                    .forEach(line -> {
                        // 逐行打印,模拟Python yield返回每行数据
                        System.out.println(Thread.currentThread().toString() + "读取到行:" + line);
                    });
            System.out.println("文件读取完成");
        } catch (IOException e) {
            // 打印完整异常栈,便于排查路径/文件问题
            System.err.println("读取文件失败:" + e.getMessage());
            e.printStackTrace();
        }
    }

    public static void main(String[] args) {
        // 1. 验证Java版本(确保支持虚拟线程)
        String javaVersion = System.getProperty("java.version");
        System.out.println("当前Java版本:" + javaVersion);

        // 2. 使用项目相对路径(避免绝对路径问题,data文件夹和generator同级)
        String filePath = Paths.get(
                "D:\\java\\my_code\\java-interview-questions\\src\\main\\java\\com\\allen\\questions\\generator\\data\\larg-file.txt"
        ).toAbsolutePath().toString();

        // 3. 启动虚拟线程,并让主线程等待虚拟线程执行完成
        Thread virtualThread = Thread.startVirtualThread(() -> readLargeFile(filePath));

        // 关键:主线程等待虚拟线程执行完毕(否则JVM直接退出)
        try {
            virtualThread.join(); // 等待虚拟线程执行完成
        } catch (InterruptedException e) {
            Thread.currentThread().interrupt();
            System.err.println("主线程被中断:" + e.getMessage());
        }
    }
}

 

posted @ 2026-02-23 10:07  Allen_Hao  阅读(25)  评论(0)    收藏  举报