Я знаю, что в самой Java нет прямого эквивалента, но, возможно, стороннего производителя?
Это действительно удобно. В настоящее время я хотел бы реализовать итератор, который выдает все узлы в дереве, что составляет около пяти строк кода с yield.
Я знаю, что в самой Java нет прямого эквивалента, но, возможно, стороннего производителя?
Это действительно удобно. В настоящее время я хотел бы реализовать итератор, который выдает все узлы в дереве, что составляет около пяти строк кода с yield.
Ответы:
Два варианта, о которых я знаю, - это библиотека infomancers-collection Aviad Ben Dov с 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 (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);
}