是否有Java等效于C#的'yield'关键字?


112

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

真的很方便。当前,我想实现一个迭代器,该迭代器生成树中的所有节点,这大约是带有yield的五行代码。


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

Answers:


91

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

两者都允许您使用yield returnJava中的-like结构编写代码,因此两者都可以满足您的要求。两者之间的显着区别是:

机械学

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使用Collector具有collect(ResultHandler)方法的泛型...嗯。但是,您可以在Zoom Information中Jim的代码使用类似这种包装的东西,从而极大地简化了:

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的解决方案是公共领域,上面提到的包装器也是如此。


3
很棒的答案。您不仅完全回答了这个问题,而且还以非常明确的方式回答了这个问题。另外,我喜欢您答案的格式以及您如何包含许可证信息。保持惊人的回答!:)
马尔科姆

1
不要忘记番石榴的AbstractIterator
shmosel

我刚刚在这里发布了另一个(由麻省理工学院许可的)解决方案,该解决方案为生产者启动了一个单独的线程,并在生产者和消费者之间建立了一个有界队列:github.com/lukehutch/Producer
Luke Hutchison

请注意,yield(i)由于JDK 13 yield已作为新的Java语句/ reserved关键字添加,因此从JDK 13开始将中断。
路加和记

14

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

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

我在这里解释了更多。


4
Yielderable?难道不是Yieldable吗?(动词只是“屈服”,而不是“屈服”或“屈服”或其他)
Jochem Kuijpers

yield -> { ... }从JDK 13开始将中断,因为yield它是作为新的Java语句/保留关键字添加的。
路加和记

1

我知道这是一个很老的问题,上面有两种描述:

  • 字节码操作在移植时并不容易;
  • 基于线程的yield资源显然很昂贵。

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


1

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
    }
}

0

我还建议您是否已经在项目中使用RXJava来将Observable用作“收益者”。如果您使自己的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
    }
}

可观察对象可以转换为迭代器,因此您甚至可以在更传统的for循环中使用它们。RXJava还为您提供了非常强大的工具,但是如果您只需要简单的东西,那也许就太过分了。


0

我刚刚在这里发布了另一个(由麻省理工学院许可的)解决方案,该解决方案在一个单独的线程中启动生产者,并在生产者和使用者之间建立了一个有界队列,从而允许在生产者和使用者之间进行缓冲,流控制和并行流水线操作(因此消费者可以在消费下一个商品的同时工作,而生产者在生产下一个商品的情况下)。

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

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);
}
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.