回答:
私が知っている2つのオプションは、2007年のAviad Ben Dovのinfomancers-collectionsライブラリと2008年のJim BlacklerのYieldAdapterライブラリです(他の回答でも説明されています)。
どちらでもyield return
、Javaで-のような構成を使用してコードを記述できるため、どちらも要求を満たします。2つの間の顕著な違いは次のとおりです。
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'sの方がはるかに複雑ですが、メソッドを持つadept
ジェネリックCollector
を使用する必要がありますcollect(ResultHandler)
...うーん。ただし、ズーム情報によるジムのコードの周りにこのラッパーのようなものを使用すると、次のことが大幅に簡素化されます。
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のソリューションはパブリックドメインであり、上記のラッパーもパブリックドメインです。
AbstractIterator
。
yield(i)
以来、JDK 13のよう壊れるyield
新しいJavaの文/予約済みキーワードとして追加されています。
JavaにLambdaが追加されたため、これらのアプローチはどちらも少しクリーンにできます。あなたは次のようなことができます
public Yielderable<Integer> oneToFive() {
return yield -> {
for (int i = 1; i < 10; i++) {
if (i == 6) yield.breaking();
yield.returning(i);
}
};
}
Yielderable
?それだけじゃないのYieldable
?(動詞は「yielder」や「yielderate」などではなく、単に「yield」です)
yield -> { ... }
はyield
新しいJavaステートメント/予約済みキーワードとして追加されているため、JDK 13以降で機能しなくなります。
ここでは非常に古い質問であることを知っています。上記の2つの方法があります。
yield
リソースコストが明らかにかかるスレッドベース。ただし、yield
C#2.0+コンパイラーがyield return/break
生成のために行うものに最も近い実装である、Javaでジェネレーターを実装する3番目の、おそらく最も自然な方法があります:lombok-pg。これは完全にステートマシンに基づいておりjavac
、ソースコードASTを操作するにはとの緊密な連携が必要です。残念ながら、lombok-pgサポートは廃止されたようで(1〜2年以上リポジトリアクティビティがない)、元のProject Lombokには残念ながらこのyield
機能がありません(ただし、EclipseのようなIDE、IntelliJ IDEAサポートが優れています)。
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
}
}
Observableを「yielder」として使用するためにプロジェクトですでにRXJavaを使用している場合もお勧めします。独自の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は非常に強力なツールを提供しますが、単純なものだけが必要な場合は、これはやり過ぎでしょう。
ここで別の(MITライセンスの)ソリューションを公開しました。これは、プロデューサーを別のスレッドで起動し、プロデューサーとコンシューマーの間に境界キューを設定して、バッファー、フロー制御、およびプロデューサーとコンシューマー間の並列パイプライン化を可能にします(生産者が次のアイテムの生産に取り組んでいる間に、消費者が前のアイテムの消費に取り組んでいる可能性があること)。
次の匿名の内部クラス形式を使用できます。
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);
}
または、ラムダ表記を使用してボイラープレートを削減できます。
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);
}