Czy istnieje Java odpowiednik słowa kluczowego C#'S 'yield'?

Wiem, że w samej Javie nie ma bezpośredniego odpowiednika, ale może jakaś trzecia strona?

To naprawdę wygodne. Obecnie chciałbym zaimplementować iterator, który daje wszystkie węzły w drzewie, czyli około pięciu linii kodu z wydajnością.

Author: ripper234, 2009-12-30

6 answers

Dwie opcje, które znam, toAviad Ben Dov 's infomancers-collections library z 2007 iJim Blackler' s yieldadapter library z 2008 (o czym również wspomniano w drugiej odpowiedzi).

Oba pozwalają na pisanie kodu z yield return-podobnie jak construct w Javie, więc oba spełnią twoje żądanie. Różnice między nimi to:

Mechanika

Biblioteka Aviada używa manipulacji kodem bajtowym, podczas gdy Jim używa wielowątkowości. W zależności na swoje potrzeby, każdy może mieć swoje zalety i wady. Prawdopodobnie rozwiązanie Aviada jest szybsze, a Jima bardziej przenośne (na przykład nie sądzę, aby Biblioteka Aviada działała na Androidzie).

Interfejs

Biblioteka Aviada ma czystszy interfejs - oto przykład:

Iterable<Integer> it = new Yielder<Integer>() {
    @Override protected void yieldNextCore() {
        for (int i = 0; i < 10; i++) {
            yieldReturn(i);
            if (i == 5) yieldBreak();
        }
    }
};

Podczas gdy Jim jest o wiele bardziej skomplikowany, wymaga od ciebie adept ogólnego Collector, który ma metodę collect(ResultHandler)... ugh. Możesz jednak użyć czegoś w rodzaju tego opakowania wokół Jima code by Zoom Information co znacznie upraszcza, że:

Iterable<Integer> it = new Generator<Integer>() {
    @Override protected void run() {
        for (int i = 0; i < 10; i++) {
            yield(i);
            if (i == 5) return;
        }
    }
};

Licencja

Rozwiązaniem Aviada jest BSD.

Rozwiązanie Jima jest domeną publiczną, podobnie jak jego opakowanie wspomniane powyżej.

 93
Author: Oak,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2013-06-19 06:47:38

Oba te podejścia mogą być nieco czystsze teraz Java ma Lambda. Możesz zrobić coś takiego jak

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

Wyjaśniłem trochę więcej tutaj.

 14
Author: benjiweber,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2015-03-21 19:47:53

Wiem, że to bardzo stare pytanie i są dwa sposoby opisane powyżej:

  • manipulacja kodem bajtowym, która nie jest taka łatwa podczas portowania;
  • thread-based {[0] } który oczywiście ma koszty zasobów.

Istnieje jednak inny, trzeci i prawdopodobnie najbardziej naturalny sposób implementacji generatora yield w Javie, który jest najbliższą implementacją do tego, co kompilatory C# 2.0+ robią dla generacji yield return/break: lombok-pg . Jest w pełni oparta na stanie maszyna i wymaga ścisłej współpracy z javac w celu manipulowania kodem źródłowym AST. Niestety, wydaje się, że wsparcie dla lombok-pg zostało przerwane (brak aktywności repozytorium przez ponad rok lub dwa), a oryginalny projekt Lombok niestety nie posiada funkcji yield (ma jednak lepsze IDE, takie jak Eclipse, IntelliJ IDEA support).

 1
Author: Lyubomyr Shaydariv,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2014-03-11 08:33:04

Strumień.iterat( seed, seedOperator).limit (n).foreach (action) nie jest tym samym co operator yield, ale może być użytecznym zapisanie własnych generatorów w ten sposób:

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
    }
}
 1
Author: Andrey Lavrukhin,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-03-29 20:45:09

Sugerowałbym również, jeśli używasz już RXJava w swoim projekcie, aby użyć obserwowalnego jako "yielder". Może być stosowany w podobny sposób, jeśli stworzysz własny obserwowalny.

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

Obiekty obserwacyjne można przekształcić w Iteratory, dzięki czemu można ich używać nawet w bardziej tradycyjnych pętlach for. Również RXJava daje naprawdę potężne narzędzia, ale jeśli potrzebujesz tylko czegoś prostego, to może byłoby to przesadą.

 1
Author: Győri Sándor,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2019-04-07 18:08:35

Właśnie opublikowałem inne (licencjonowane przez MIT) rozwiązanie tutaj , które uruchamia producenta w oddzielnym wątku i ustawia ograniczoną kolejkę między producentem a konsumentem, umożliwiając buforowanie, kontrolę przepływu i równoległe rurociągi między producentem a konsumentem (tak, że konsument może pracować nad spożywaniem poprzedniego produktu, podczas gdy producent pracuje nad produkcją następnego produktu).

Możesz użyć tej anonimowej wewnętrznej formy klasy:

Iterable<T> iterable = new Producer<T>(queueSize) {
    @Override
    public void producer() {
        produce(someT);
    }
};

Dla przykład:

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

Lub możesz użyć notacji lambda, aby wyciąć na kotle:

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);
}
 0
Author: Luke Hutchison,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2019-12-17 00:29:21