Я знаю, что в самой Java нет прямого эквивалента, но, может быть, стороннего производителя?
Это действительно удобно. В настоящее время я хотел бы реализовать итератор, который выдает все узлы в дереве, что составляет около пяти строк кода с yield.
Я знаю, что в самой Java нет прямого эквивалента, но, может быть, стороннего производителя?
Это действительно удобно. В настоящее время я хотел бы реализовать итератор, который выдает все узлы в дереве, что составляет около пяти строк кода с yield.
Мне известны два варианта: Библиотека информационных материалов-коллекций Авиада Бен Дова 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.
Решение Джима является общественным достоянием, как и его оболочка, упомянутая выше.
AbstractIterator
от Гуавы. .
- person shmosel; 31.08.2017
Оба этих подхода можно сделать немного чище, теперь в Java есть Lambdas. Вы можете сделать что-то вроде
public Yielderable<Integer> oneToFive() {
return yield -> {
for (int i = 1; i < 10; i++) {
if (i == 6) yield.breaking();
yield.returning(i);
}
};
}
Я объяснил здесь немного больше.
Yielderable
? Разве это не должно быть просто Yieldable
? (глагол просто «уступать», а не «уступать» или «уступать» или что-то еще)
- person Jochem Kuijpers; 25.10.2018
yield -> { ... }
не работает с JDK 13, поскольку yield
добавляется как новый оператор Java / зарезервированное ключевое слово.
- person Luke Hutchison; 17.12.2019
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).
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
}
}
Я бы также посоветовал, если вы уже используете 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 дает вам действительно мощные инструменты, но если вам нужно только что-то простое, возможно, это будет излишним.
Я только что опубликовал еще одно решение (под лицензией 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);
}