Есть ли в Java эквивалент ключевого слова yield в C #?

Я знаю, что в самой Java нет прямого эквивалента, но, может быть, стороннего производителя?

Это действительно удобно. В настоящее время я хотел бы реализовать итератор, который выдает все узлы в дереве, что составляет около пяти строк кода с yield.


person ripper234    schedule 30.12.2009    source источник
comment
Знаю, знаю. Но я думаю, что знание большего количества языков - большая сила. Кроме того, внутренняя разработка (которой я занимаюсь) в компании, в которой я сейчас работаю, выполняется на Java, поэтому я не могу выбрать язык :(   -  person ripper234    schedule 30.12.2009


Ответы (6)


Мне известны два варианта: Библиотека информационных материалов-коллекций Авиада Бен Дова 2007 года и Библиотека YieldAdapter Джима Блэклера из 2008 года (которая также упоминается в другом ответе).

Оба позволят вам написать код с yield return-подобной конструкцией на Java, поэтому оба будут удовлетворять ваш запрос. Заметные различия между ними:

Механика

Библиотека Aviad использует манипуляции с байт-кодом, в то время как библиотека Джима использует многопоточность. В зависимости от ваших потребностей у каждого могут быть свои преимущества и недостатки. Вероятно, решение Aviad быстрее, а решение Джима более портативно (например, я не думаю, что библиотека 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();
        }
    }
};

В то время как у Джима намного сложнее, требуя от вас adept общий Collector, который имеет collect(ResultHandler) метод ... тьфу. Однако вы можете использовать что-то вроде этой оболочки вокруг кода Джима от 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.

Решение Джима является общественным достоянием, как и его оболочка, упомянутая выше.

person Oak    schedule 18.06.2013
comment
Фантастический ответ. Вы не только полностью ответили на вопрос, но и сделали это очень четко. Кроме того, мне нравится формат вашего ответа и то, как вы включили информацию о лицензии. Продолжайте отвечать на вопросы! :) - person Malcolm; 07.08.2013
comment
Не забудьте AbstractIterator от Гуавы. . - person shmosel; 31.08.2017
comment
Я только что опубликовал здесь другое решение (под лицензией MIT), которое запускает отдельный поток для производителя и устанавливает ограниченную очередь между производителем и потребителем: github.com/lukehutch/Producer - person Luke Hutchison; 17.12.2019

Оба этих подхода можно сделать немного чище, теперь в Java есть Lambdas. Вы можете сделать что-то вроде

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

Я объяснил здесь немного больше.

person benjiweber    schedule 21.03.2015
comment
Yielderable? Разве это не должно быть просто Yieldable? (глагол просто «уступать», а не «уступать» или «уступать» или что-то еще) - person Jochem Kuijpers; 25.10.2018
comment
yield -> { ... } не работает с JDK 13, поскольку yield добавляется как новый оператор Java / зарезервированное ключевое слово. - person Luke Hutchison; 17.12.2019
comment
@LukeHutchison, нет. Только вызовы метода с именем «yield» требуют, чтобы уточняющее выражение или тип отличалось от оператора yield. Присвоение имени переменной yield и ее использование не требует никаких изменений. - person Holger; 30.09.2020

Я знаю, что это очень старый вопрос, и есть два способа, описанных выше:

  • манипулирование байт-кодом, которое не так просто при портировании;
  • основанный на потоках yield, который, очевидно, требует затрат ресурсов.

Однако есть другой, третий и, вероятно, наиболее естественный способ реализации генератора yield в Java, который является наиболее близкой реализацией к тому, что компиляторы C # 2.0+ делают для поколения yield return/break: lombok-pg. Он полностью основан на конечном автомате и требует тесного сотрудничества с javac для управления исходным кодом AST. К сожалению, поддержка lombok-pg, похоже, прекращена (репозиторий неактивен более года или двух), а исходный Project Lombok, к сожалению, не имеет функции yield (хотя у него есть лучшая IDE, такая как Eclipse, но с поддержкой IntelliJ IDEA).

person Lyubomyr Shaydariv    schedule 11.03.2014

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
    }
}
person Andrey Lavrukhin    schedule 29.03.2017

Я бы также посоветовал, если вы уже используете 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 дает вам действительно мощные инструменты, но если вам нужно только что-то простое, возможно, это будет излишним.

person Győri Sándor    schedule 07.04.2019

Я только что опубликовал еще одно решение (под лицензией 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);
}
person Luke Hutchison    schedule 28.10.2019