是否有相当于 C# 的“yield”关键字的 Java?
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/1980953/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
Is there a Java equivalent to C#'s 'yield' keyword?
提问by ripper234
I know there is no direct equivalent in Java itself, but perhaps a third party?
我知道 Java 本身没有直接的等价物,但也许是第三方?
It is really convenient. Currently I'd like to implement an iterator that yields all nodes in a tree, which is about five lines of code with yield.
这真的很方便。目前我想实现一个生成树中所有节点的迭代器,大约有五行代码。
采纳答案by Oak
The two options I know of is Aviad Ben Dov's infomancers-collections library from 2007and Jim Blackler's YieldAdapter library from 2008(which is also mentioned in the other answer).
我知道的两个选项是 2007 年的Aviad Ben Dov 的 infomancers-collections 库和2008 年的 Jim Blackler 的 YieldAdapter 库(在另一个答案中也提到过)。
Both will allow you to write code with yield return
-like construct in Java, so both will satisfy your request. The notable differences between the two are:
两者都允许您yield return
在 Java 中使用-like 构造编写代码,因此两者都将满足您的要求。两者之间显着的区别是:
Mechanics
力学
Aviad's library is using bytecode manipulation while Jim's uses multithreading. Depending on your needs, each may have its own advantages and disadvantages. It's likely Aviad's solution is faster, while Jim's is more portable (for example, I don't think Aviad's library will work on Android).
Aviad 的库使用字节码操作,而 Jim 的库使用多线程。根据您的需求,每种可能都有自己的优点和缺点。Aviad 的解决方案可能更快,而 Jim 的解决方案更便携(例如,我认为 Aviad 的库不适用于 Android)。
Interface
界面
Aviad's library has a cleaner interface - here's an example:
Aviad 的库有一个更清晰的界面——这是一个例子:
Iterable<Integer> it = new Yielder<Integer>() {
@Override protected void yieldNextCore() {
for (int i = 0; i < 10; i++) {
yieldReturn(i);
if (i == 5) yieldBreak();
}
}
};
While Jim's is way more complicated, requiring you to adept
a generic Collector
which has a collect(ResultHandler)
method... ugh. However, you could use something like this wrapper around Jim's code by Zoom Informationwhich greatly simplifies that:
虽然 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;
}
}
};
License
执照
Aviad's solution is BSD.
Aviad 的解决方案是 BSD。
Jim's solution is public domain, and so is its wrapper mentioned above.
Jim 的解决方案是公共领域的,上面提到的包装器也是如此。
回答by Lyubomyr Shaydariv
I know it's a very old question here, and there are two ways described above:
我知道这是一个非常古老的问题,上面描述了两种方法:
- bytecode manipulation that's not that easy while porting;
- thread-based
yield
that obviously has resource costs.
- 移植时不太容易的字节码操作;
- 基于线程的
yield
显然具有资源成本。
However, there is another, the third and probably the most natural, way of implementing the yield
generator in Java that is the closest implementation to what C# 2.0+ compilers do for yield return/break
generation: lombok-pg. It's fully based on a state machine, and requires tight cooperation with javac
to manipulate the source code AST. Unfortunately, the lombok-pg support seems to be discontinued (no repository activity for more than a year or two), and the original Project Lombokunfortunately lacks the yield
feature (it has better IDE like Eclipse, IntelliJ IDEA support, though).
然而,还有另一种,第三种,也可能是最自然的,yield
在 Java中实现生成器的方法,它是最接近 C# 2.0+ 编译器为yield return/break
生成所做的实现:lombok-pg。它完全基于状态机,需要与javac
AST紧密合作才能操作源代码。不幸的是,lombok-pg 支持似乎已停止(超过一两年没有存储库活动),而原始的Project Lombok不幸的是缺少该yield
功能(尽管它具有更好的 IDE,如 Eclipse,但支持 IntelliJ IDEA)。
回答by benjiweber
Both of these approaches can be made a bit cleaner now Java has Lambdas. You can do something like
现在 Java 有了 Lambda,这两种方法都可以变得更简洁。你可以做类似的事情
public Yielderable<Integer> oneToFive() {
return yield -> {
for (int i = 1; i < 10; i++) {
if (i == 6) yield.breaking();
yield.returning(i);
}
};
}
回答by Andrey Lavrukhin
Stream.iterate(seed, seedOperator).limit(n).foreach(action)is not the same as yield operator, but it may be usefull to write your own generators this way:
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
}
}
回答by Gy?ri Sándor
I'd also suggest if you're already using RXJavain your project to use an Observable as a "yielder". It can be used in a similar fashion if you make your own Observable.
我还建议您是否已经在您的项目中使用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 can be transformed into iterators so you can even use them in more traditional for loops. Also RXJava gives you really powerful tools, but if you only need something simple then maybe this would be an overkill.
Observable 可以转换为迭代器,因此您甚至可以在更传统的 for 循环中使用它们。RXJava 也为您提供了非常强大的工具,但如果您只需要一些简单的东西,那么这可能是一种矫枉过正。
回答by Luke Hutchison
I just published another (MIT-licensed) solution here, which launches the producer in a separate thread, and sets up a bounded queue between the producer and the consumer, allowing for buffering, flow control, and parallel pipelining between producer and consumer (so that the consumer can be working on consuming the previous item while the producer is working on producing the next item).
我刚刚在这里发布了另一个(MIT 许可的)解决方案,它在一个单独的线程中启动生产者,并在生产者和消费者之间设置一个有界队列,允许在生产者和消费者之间进行缓冲、流量控制和并行流水线(所以消费者可以在生产者正在生产下一个项目的同时消费前一个项目)。
You can use this anonymous inner class form:
你可以使用这个匿名内部类形式:
Iterable<T> iterable = new Producer<T>(queueSize) {
@Override
public void producer() {
produce(someT);
}
};
for example:
例如:
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);
}
Or you can use lambda notation to cut down on boilerplate:
或者您可以使用 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);
}