Я знаю, що в самій Java немає прямого еквівалента, але, можливо, третя сторона?
Це дійсно зручно. В даний час я хотів би реалізувати ітератор, який видає всі вузли на дереві, тобто приблизно п’ять рядків коду з урожайністю.
Я знаю, що в самій Java немає прямого еквівалента, але, можливо, третя сторона?
Це дійсно зручно. В даний час я хотів би реалізувати ітератор, який видає всі вузли на дереві, тобто приблизно п’ять рядків коду з урожайністю.
Відповіді:
Мені відомі два варіанти - бібліотека колекцій рекламних колективів Авіада Бен Дова з 2007 року та бібліотека YieldAdapter Джима Блеклера з 2008 року (про що також йдеться в іншій відповіді).
І yield return
те й інше дозволить вам писати код на конструкції -like на 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
.
yield(i)
буде розбита з JDK 13, оскільки yield
додається як новий оператор Java / зарезервоване ключове слово.
Обидва ці підходи можна зробити трохи чистішими, тепер у 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
? (дієслово просто «врожайність», а не «врожай» або «врожай» чи інше)
yield -> { ... }
розірветься з JDK 13, оскільки yield
додається як новий оператор Java / зарезервоване ключове слово.
Я знаю, що тут дуже давнє питання, і є два способи, описані вище:
yield
що, очевидно, має ресурсні витрати.Однак існує ще один, третій і, мабуть, самий природний спосіб реалізації yield
генератора на Java, який є найближчим до виконання тим, що компілятори C # 2.0+ роблять для yield return/break
покоління: lombok-pg . Він повністю базується на державній машині і вимагає тісної співпраці з javac
маніпулюванням вихідним кодом AST. На жаль, підтримка lombok-pg, здається, припинена (відсутність активності сховища більше року-двох), а в оригінальному Project Lombok, на жаль, немає такої yield
функції (хоча вона має кращу IDE, як Eclipse, підтримка IntelliJ IDEA).
Stream.iterate (насіння, seedOperator) .limit (n) .foreach (дія) не є тим самим, як оператор урожайності, але може бути корисним записати власні генератори таким чином:
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 як "поступник". Це можна використовувати аналогічно, якщо ви зробите свій власний Спостережний.
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
}
}
Спостережні дані можуть бути перетворені в ітератори, так що ви навіть можете використовувати їх у більш традиційних для циклів. Також 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);
}