So che non esiste un equivalente diretto in Java stesso, ma forse una terza parte?
È davvero conveniente. Attualmente vorrei implementare un iteratore che restituisca tutti i nodi in un albero, che è di circa cinque righe di codice con rendimento.
So che non esiste un equivalente diretto in Java stesso, ma forse una terza parte?
È davvero conveniente. Attualmente vorrei implementare un iteratore che restituisca tutti i nodi in un albero, che è di circa cinque righe di codice con rendimento.
Risposte:
Le due opzioni che conosco sono la libreria di collezioni di infomanti di Aviad Ben Dov del 2007 e la libreria YieldAdapter di Jim Blackler del 2008 (che è anche menzionata nell'altra risposta).
Entrambi ti permetteranno di scrivere codice con il yield return
costrutto -like in Java, quindi entrambi soddisferanno la tua richiesta. Le differenze notevoli tra i due sono:
La libreria di Aviad utilizza la manipolazione del bytecode mentre quella di Jim utilizza il multithreading. A seconda delle tue esigenze, ognuna può avere i suoi vantaggi e svantaggi. È probabile che la soluzione di Aviad sia più veloce, mentre quella di Jim è più portabile (ad esempio, non credo che la libreria di Aviad funzionerà su Android).
La libreria di Aviad ha un'interfaccia più pulita: ecco un esempio:
Iterable<Integer> it = new Yielder<Integer>() {
@Override protected void yieldNextCore() {
for (int i = 0; i < 10; i++) {
yieldReturn(i);
if (i == 5) yieldBreak();
}
}
};
Mentre quello di Jim è molto più complicato, ti richiede adept
un generico Collector
che abbia un collect(ResultHandler)
metodo ... ugh. Tuttavia, potresti usare qualcosa come questo wrapper attorno al codice di Jim di Zoom Information che semplifica notevolmente che:
Iterable<Integer> it = new Generator<Integer>() {
@Override protected void run() {
for (int i = 0; i < 10; i++) {
yield(i);
if (i == 5) return;
}
}
};
La soluzione di Aviad è BSD.
La soluzione di Jim è di pubblico dominio, così come il suo wrapper menzionato sopra.
AbstractIterator
.
yield(i)
si interromperà a partire da JDK 13, poiché yield
viene aggiunto come nuova istruzione Java / parola chiave riservata.
Entrambi questi approcci possono essere resi un po 'più puliti ora che Java ha Lambdas. Puoi fare qualcosa di simile
public Yielderable<Integer> oneToFive() {
return yield -> {
for (int i = 1; i < 10; i++) {
if (i == 6) yield.breaking();
yield.returning(i);
}
};
}
Yielderable
? Non dovrebbe essere solo Yieldable
? (il verbo è solo 'cedere', non 'prodigare' o 'prodigare' o qualsiasi altra cosa)
yield -> { ... }
si interromperà a partire da JDK 13, poiché yield
viene aggiunto come nuova istruzione Java / parola chiave riservata.
So che qui è una domanda molto vecchia e ci sono due modi descritti sopra:
yield
che ovviamente ha costi in risorse.Tuttavia, c'è un altro modo, il terzo e probabilmente il più naturale, di implementare il yield
generatore in Java che è l'implementazione più vicina a ciò che i compilatori C # 2.0+ fanno per la yield return/break
generazione: lombok-pg . È completamente basato su una macchina a stati e richiede una stretta collaborazione javac
per manipolare il codice sorgente AST. Sfortunatamente, il supporto lombok-pg sembra essere interrotto (nessuna attività di repository per più di un anno o due), e l'originale Project Lombok purtroppo manca della yield
funzionalità (ha un IDE migliore come Eclipse, supporto IntelliJ IDEA, però).
Stream.iterate (seed, seedOperator) .limit (n) .foreach (action) non è la stessa cosa dell'operatore yield, ma potrebbe essere utile scrivere i propri generatori in questo modo:
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
}
}
Suggerirei anche se stai già usando RXJava nel tuo progetto di usare un Observable come "yielder". Può essere utilizzato in modo simile se crei il tuo 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
}
}
Gli osservabili possono essere trasformati in iteratori in modo da poterli persino utilizzare in cicli for più tradizionali. Inoltre RXJava ti offre strumenti davvero potenti, ma se hai solo bisogno di qualcosa di semplice, forse questo sarebbe eccessivo.
Ho appena pubblicato un'altra soluzione (con licenza MIT) qui , che avvia il produttore in un thread separato e imposta una coda delimitata tra il produttore e il consumatore, consentendo il buffering, il controllo del flusso e il pipelining parallelo tra produttore e consumatore (quindi che il consumatore può lavorare sul consumo dell'articolo precedente mentre il produttore sta lavorando sulla produzione dell'articolo successivo).
Puoi usare questo modulo anonimo della classe interna:
Iterable<T> iterable = new Producer<T>(queueSize) {
@Override
public void producer() {
produce(someT);
}
};
per esempio:
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);
}
Oppure puoi usare la notazione lambda per ridurre il boilerplate:
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);
}