C#の「yield」キーワードに相当するJavaはありますか?


112

Java自体には直接の同等物がないことを知っていますが、おそらくサードパーティですか?

本当に便利です。現在、ツリー内のすべてのノードを生成するイテレーターを実装したいと思います。これは、yieldを含む約5行のコードです。


6
分かった分かった。しかし、私はより多くの言語を知ることがより強力だと思います。さらに、私が現在取り組んでいる会社のバックエンド開発(私がやっていること)はJavaで行われているので、言語を実際に選択することはできません:(
ripper234

回答:


91

私が知っている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のソリューションはパブリックドメインであり、上記のラッパーもパブリックドメインです。


3
素晴らしい答え。質問に完全に答えただけでなく、非常に明確に答えました。また、回答の形式とライセンス情報をどのように含めたかが気に入っています。すばらしい回答を続けましょう!:)
Malcolm

1
グアバのを忘れないでくださいAbstractIterator
shmosel 2017

私はここで別の(MITライセンスの)ソリューションを公開しました。これは、プロデューサーに対して個別のスレッドを起動し、プロデューサーとコンシューマーの間に制限付きキューを設定します。github.com/ lukehutch / Producer
Luke Hutchison

yield(i)以来、JDK 13のよう壊れるyield新しいJavaの文/予約済みキーワードとして追加されています。
ルークハッチソン

14

JavaにLambdaが追加されたため、これらのアプローチはどちらも少しクリーンにできます。あなたは次のようなことができます

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

ここでもう少し説明しました。


4
Yielderable?それだけじゃないのYieldable?(動詞は「yielder」や「yielderate」などではなく、単に「yield」です)
Jochem Kuijpers

yield -> { ... }yield新しいJavaステートメント/予約済みキーワードとして追加されているため、JDK 13以降で機能しなくなります。
ルークハッチソン

1

ここでは非常に古い質問であることを知っています。上記の2つの方法があります。

  • 移植時にそれほど簡単ではないバイトコード操作。
  • yieldリソースコストが明らかにかかるスレッドベース。

ただし、yieldC#2.0+コンパイラーがyield return/break生成のために行うものに最も近い実装である、Javaでジェネレーターを実装する3番目の、おそらく最も自然な方法があります:lombok-pg。これは完全にステートマシンに基づいておりjavac、ソースコードASTを操作するにはとの緊密な連携が必要です。残念ながら、lombok-pgサポートは廃止されたようで(1〜2年以上リポジトリアクティビティがない)、元のProject Lombokには残念ながらこのyield機能がありません(ただし、EclipseのようなIDE、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

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は非常に強力なツールを提供しますが、単純なものだけが必要な場合は、これはやり過ぎでしょう。


0

ここで別の(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);
}
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.