Ich weiß, dass es in Java selbst kein direktes Äquivalent gibt, aber vielleicht einen Dritten?
Es ist wirklich praktisch. Derzeit möchte ich einen Iterator implementieren, der alle Knoten in einem Baum ergibt, dh etwa fünf Codezeilen mit Ausbeute.
Ich weiß, dass es in Java selbst kein direktes Äquivalent gibt, aber vielleicht einen Dritten?
Es ist wirklich praktisch. Derzeit möchte ich einen Iterator implementieren, der alle Knoten in einem Baum ergibt, dh etwa fünf Codezeilen mit Ausbeute.
Antworten:
Die beiden mir bekannten Optionen sind die Infomancers-Collections-Bibliothek von Aviad Ben Dov aus dem Jahr 2007 und die YieldAdapter-Bibliothek von Jim Blackler aus dem Jahr 2008 (die auch in der anderen Antwort erwähnt wird).
Mit beiden können Sie Code mit einem yield return
ähnlichen Konstrukt in Java schreiben , sodass beide Ihre Anforderung erfüllen. Die bemerkenswerten Unterschiede zwischen den beiden sind:
Die Bibliothek von Aviad verwendet die Bytecode-Manipulation, während die von Jim Multithreading verwendet. Abhängig von Ihren Anforderungen kann jeder seine eigenen Vor- und Nachteile haben. Es ist wahrscheinlich, dass die Lösung von Aviad schneller ist, während die von Jim portabler ist (zum Beispiel glaube ich nicht, dass die Bibliothek von Aviad unter Android funktioniert).
Die Bibliothek von Aviad verfügt über eine übersichtlichere Benutzeroberfläche - hier ein Beispiel:
Iterable<Integer> it = new Yielder<Integer>() {
@Override protected void yieldNextCore() {
for (int i = 0; i < 10; i++) {
yieldReturn(i);
if (i == 5) yieldBreak();
}
}
};
Während Jims viel komplizierter ist, müssen Sie adept
ein Generikum verwenden, Collector
das eine collect(ResultHandler)
Methode hat ... ugh. Sie könnten jedoch so etwas wie diesen Wrapper um Jims Code von Zoom Information verwenden, was das erheblich vereinfacht:
Iterable<Integer> it = new Generator<Integer>() {
@Override protected void run() {
for (int i = 0; i < 10; i++) {
yield(i);
if (i == 5) return;
}
}
};
Die Lösung von Aviad ist BSD.
Jims Lösung ist gemeinfrei, ebenso wie der oben erwähnte Wrapper.
AbstractIterator
.
yield(i)
ab JDK 13 nicht mehr funktioniert, da yield
es als neue Java-Anweisung / reserviertes Schlüsselwort hinzugefügt wird.
Beide Ansätze können jetzt etwas sauberer gemacht werden, da Java Lambdas hat. Sie können so etwas tun
public Yielderable<Integer> oneToFive() {
return yield -> {
for (int i = 1; i < 10; i++) {
if (i == 6) yield.breaking();
yield.returning(i);
}
};
}
Yielderable
? Sollte es nicht einfach so sein Yieldable
? (Das Verb ist nur "Ausbeute", nicht "Ertrag" oder "Ertrag" oder was auch immer)
yield -> { ... }
wird ab JDK 13 unterbrochen, da yield
es als neue Java-Anweisung / reserviertes Schlüsselwort hinzugefügt wird.
Ich weiß, dass es hier eine sehr alte Frage ist, und es gibt zwei oben beschriebene Möglichkeiten:
yield
, das hat offensichtlich Ressourcenkosten.Es gibt jedoch eine andere, dritte und wahrscheinlich natürlichste Möglichkeit, den yield
Generator in Java zu implementieren, die der Implementierung von C # 2.0+ -Compilern zur yield return/break
Generierung am nächsten kommt : lombok-pg . Es basiert vollständig auf einer Zustandsmaschine und erfordert eine enge Zusammenarbeit javac
, um den Quellcode AST zu manipulieren. Leider scheint die lombok-pg-Unterstützung eingestellt zu sein (seit mehr als ein oder zwei Jahren keine Repository-Aktivität mehr), und dem ursprünglichen Projekt Lombok fehlt leider die yield
Funktion (es hat jedoch eine bessere IDE wie Eclipse, IntelliJ IDEA-Unterstützung).
Stream.iterate (seed, seedOperator) .limit (n) .foreach (action) ist nicht dasselbe wie Yield Operator, aber es kann nützlich sein, eigene Generatoren folgendermaßen zu schreiben:
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
}
}
Ich würde auch vorschlagen, wenn Sie RXJava bereits in Ihrem Projekt verwenden, um ein Observable als "Yielder" zu verwenden. Es kann auf ähnliche Weise verwendet werden, wenn Sie Ihr eigenes Observable erstellen.
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
}
}
Observables können in Iteratoren umgewandelt werden, sodass Sie sie sogar in traditionelleren for-Schleifen verwenden können. Auch RXJava bietet Ihnen wirklich leistungsstarke Tools, aber wenn Sie nur etwas Einfaches benötigen, wäre dies möglicherweise ein Overkill.
Ich habe hier gerade eine andere (MIT-lizenzierte) Lösung veröffentlicht , die den Produzenten in einem separaten Thread startet und eine begrenzte Warteschlange zwischen dem Produzenten und dem Konsumenten einrichtet , die Pufferung, Flusskontrolle und paralleles Pipelining zwischen Produzent und Konsument ermöglicht (so) dass der Verbraucher daran arbeiten kann, den vorherigen Artikel zu konsumieren, während der Produzent daran arbeitet, den nächsten Artikel zu produzieren).
Sie können dieses anonyme Formular für die innere Klasse verwenden:
Iterable<T> iterable = new Producer<T>(queueSize) {
@Override
public void producer() {
produce(someT);
}
};
beispielsweise:
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);
}
Oder Sie können die Lambda-Notation verwenden, um die Boilerplate zu reduzieren:
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);
}