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

时间:2009-12-30 16:08:35

标签: java yield yield-return

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

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

7 个答案:

答案 0 :(得分:88)

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

两者都允许您使用yield return编写代码 - 就像Java中的构造一样,因此两者都将满足您的请求。两者之间的显着差异是:

力学

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) ......呃。但是,您可以使用类似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的解决方案是公共领域,上面提到的包装器也是如此。

答案 1 :(得分:12)

现在,Java拥有Lambdas,这两种方法都可以变得更加清晰。你可以做点什么

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.

答案 2 :(得分:1)

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

  • 移植时的字节码操作并不容易;
  • 基于线程的yield显然有资源成本。

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

答案 3 :(得分:1)

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

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

答案 4 :(得分: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还为您提供了非常强大的工具,但是如果您只需要简单的东西,那也许就太过分了。

答案 5 :(得分:0)

我刚刚发布了另一种(由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);
}

答案 6 :(得分:-7)

在Java 8中,您可以使用:Stream.of