ChatGPT解决这个技术问题 Extra ChatGPT

是否有与 C# 的“yield”关键字等效的 Java?

我知道 Java 本身没有直接的等价物,但也许是第三方?

真的很方便。目前我想实现一个迭代器,它产生树中的所有节点,大约有五行代码。

我知道我知道。但我认为了解更多语言会更有力量。此外,我现在工作的公司的后端开发(我正在做的)是用 Java 完成的,所以我真的不能选择语言:(

O
Oak

我知道的两个选项是 Aviad Ben Dov's infomancers-collections library from 2007Jim Blackler's YieldAdapter library from 2008 (在另一个答案中也提到了)。

两者都允许您在 Java 中使用类似 yield return 的构造编写代码,因此两者都将满足您的要求。两者之间的显着差异是:

力学

Aviad 的库使用字节码操作,而 Jim 的库使用多线程。根据您的需要,每个可能都有自己的优点和缺点。很可能 Aviad 的解决方案更快,而 Jim 的解决方案更便携(例如,我认为 Aviad 的库不能在 Android 上运行)。

界面

Aviad 的库具有更简洁的界面 - 这是一个示例:

Iterable<Integer> it = new Yielder<Integer>() {
    @Override protected void yieldNextCore() {
        for (int i = 0; i < 10; i++) {
            yieldReturn(i);
            if (i == 5) yieldBreak();
        }
    }
};

虽然 Jim 的方法要复杂得多,但需要您adept 一个具有 collect(ResultHandler) 方法的通用 Collector...呃。但是,您可以使用 this wrapper around Jim's code by Zoom Information 之类的东西来大大简化:

Iterable<Integer> it = new Generator<Integer>() {
    @Override protected void run() {
        for (int i = 0; i < 10; i++) {
            yield(i);
            if (i == 5) return;
        }
    }
};

执照

Aviad 的解决方案是 BSD。

Jim 的解决方案是公共领域,上面提到的它的包装器也是如此。


很棒的答案。你不仅完全回答了这个问题,而且回答得很清楚。另外,我喜欢您的答案格式以及您如何包含许可证信息。继续精彩的回答! :)
不要忘记 Guava 的 AbstractIterator
我刚刚在这里发布了另一个(MIT 许可)解决方案,它为生产者启动了一个单独的线程,并在生产者和消费者之间建立了一个有界队列:github.com/lukehutch/Producer
b
benjiweber

现在 Java 有了 Lambda,这两种方法都可以变得更加简洁。你可以做类似的事情

public Yielderable<Integer> oneToFive() {
    return yield -> {
        for (int i = 1; i < 10; i++) {
            if (i == 6) yield.breaking();
            yield.returning(i);
        }
    };
}

I explained a bit more here.


Yielderable?不应该只是Yieldable吗? (动词只是'yield',而不是'yielder'或'yielderate'或其他)
yield -> { ... } 将从 JDK 13 开始中断,因为 yield 被添加为新的 Java 语句/保留关键字。
@LukeHutchison 不,不是。只有调用名为“yield”的方法才需要限定表达式或类型才能与 yield 语句区分开来。命名变量 yield 并使用它不需要任何更改。
L
Lyubomyr Shaydariv

我知道这是一个非常古老的问题,上面描述了两种方法:

移植时不那么容易的字节码操作;

显然有资源成本的基于线程的产量。

但是,还有另一种,第三种,可能是最自然的在 Java 中实现 yield 生成器的方法,它是最接近 C# 2.0+ 编译器为 yield return/break 生成所做的实现:lombok-pg。它完全基于状态机,需要与 javac 紧密合作来操作源代码 AST。不幸的是,lombok-pg 支持似乎已停止(超过一两年没有存储库活动),不幸的是原始 Project Lombok 缺少 yield 功能(它具有更好的 IDE,如 Eclipse,IntelliJ IDEA 支持,不过)。


A
Andrey Lavrukhin

Stream.iterate(seed, seedOperator).limit(n).foreach(action) 与 yield 运算符不同,但以这种方式编写自己的生成器可能会很有用:

import java.util.stream.Stream;
public class Test01 {
    private static void myFoo(int someVar){
        //do some work
        System.out.println(someVar);
    }
    private static void myFoo2(){
        //do some work
        System.out.println("some work");
    }
    public static void main(String[] args) {
        Stream.iterate(1, x -> x + 1).limit(15).forEach(Test01::myFoo);     //var1
        Stream.iterate(1, x -> x + 1).limit(10).forEach(item -> myFoo2());  //var2
    }
}

G
Győri Sándor

如果您已经在项目中使用 RXJava,我还建议您使用 Observable 作为“yielder”。如果您制作自己的 Observable,它可以以类似的方式使用。

public class Example extends Observable<String> {

    public static void main(String[] args) {
        new Example().blockingSubscribe(System.out::println); // "a", "b", "c", "d"
    }

    @Override
    protected void subscribeActual(Observer<? super String> observer) {
        observer.onNext("a"); // yield
        observer.onNext("b"); // yield
        observer.onNext("c"); // yield
        observer.onNext("d"); // yield
        observer.onComplete(); // finish
    }
}

Observables 可以转换为迭代器,因此您甚至可以在更传统的 for 循环中使用它们。 RXJava 还为您提供了非常强大的工具,但如果您只需要一些简单的东西,那么这可能是一种矫枉过正。


L
Luke Hutchison

我刚刚发布了另一个(MIT 许可)解决方案 here,它在单独的线程中启动生产者,并在生产者和消费者之间建立了一个有界队列,允许在生产者和消费者之间进行缓冲、流控制和并行管道(这样消费者可以在生产者生产下一个项目的同时消费前一个项目)。

您可以使用这种匿名内部类形式:

Iterable<T> iterable = new Producer<T>(queueSize) {
    @Override
    public void producer() {
        produce(someT);
    }
};

例如:

for (Integer item : new Producer<Integer>(/* queueSize = */ 5) {
    @Override
    public void producer() {
        for (int i = 0; i < 20; i++) {
            System.out.println("Producing " + i);
            produce(i);
        }
        System.out.println("Producer exiting");
    }
}) {
    System.out.println("  Consuming " + item);
    Thread.sleep(200);
}

或者您可以使用 lambda 表示法来减少样板:

for (Integer item : new Producer<Integer>(/* queueSize = */ 5, producer -> {
    for (int i = 0; i < 20; i++) {
        System.out.println("Producing " + i);
        producer.produce(i);
    }
    System.out.println("Producer exiting");
})) {
    System.out.println("  Consuming " + item);
    Thread.sleep(200);
}

R
RandomB
// Java code for Stream.generate()
// to generate an infinite sequential
// unordered stream
import java.util.*;
import java.util.stream.Stream;
  
class GFG {
      
    // Driver code
    public static void main(String[] args) {
      
    // using Stream.generate() method 
    // to generate 5 random Integer values
    Stream.generate(new Random()::nextInt)
      .limit(5).forEach(System.out::println); 
    }
}

From here