Wiem, że w samej Javie nie ma bezpośredniego odpowiednika, ale być może strona trzecia?
To naprawdę wygodne. Obecnie chciałbym zaimplementować iterator, który zwraca wszystkie węzły w drzewie, czyli około pięciu wierszy kodu z wydajnością.
Wiem, że w samej Javie nie ma bezpośredniego odpowiednika, ale być może strona trzecia?
To naprawdę wygodne. Obecnie chciałbym zaimplementować iterator, który zwraca wszystkie węzły w drzewie, czyli około pięciu wierszy kodu z wydajnością.
Odpowiedzi:
Dwie opcje znam to Aviad Ben Dov za infomancers-zbiory biblioteki od 2007 i Jim Blackler za YieldAdapter biblioteka z 2008 roku (który jest również wymienione w drugiej odpowiedzi).
Oba pozwolą ci pisać kod z yield return
konstrukcją podobną do typu w Javie, więc oba spełnią twoje żądanie. Istotne różnice między nimi to:
Biblioteka Aviad używa manipulacji kodem bajtowym, podczas gdy Jim używa wielowątkowości. W zależności od potrzeb każdy może mieć swoje zalety i wady. Prawdopodobnie rozwiązanie Aviad jest szybsze, podczas gdy Jim jest bardziej przenośny (na przykład nie sądzę, aby biblioteka Aviad działała na Androidzie).
Biblioteka Aviad ma bardziej przejrzysty interfejs - oto przykład:
Iterable<Integer> it = new Yielder<Integer>() {
@Override protected void yieldNextCore() {
for (int i = 0; i < 10; i++) {
yieldReturn(i);
if (i == 5) yieldBreak();
}
}
};
Podczas gdy Jim jest o wiele bardziej skomplikowany, wymaga od ciebie adept
generycznego, Collector
który ma collect(ResultHandler)
metodę ... ugh. Możesz jednak użyć czegoś takiego jak ta otoka wokół kodu Jima autorstwa Zoom Information, co znacznie upraszcza to:
Iterable<Integer> it = new Generator<Integer>() {
@Override protected void run() {
for (int i = 0; i < 10; i++) {
yield(i);
if (i == 5) return;
}
}
};
Rozwiązaniem Aviad jest BSD.
Rozwiązanie Jima jest własnością publiczną, podobnie jak jego opakowanie, o którym wspomniano powyżej.
AbstractIterator
.
yield(i)
zepsuje się od JDK 13, ponieważ yield
jest dodawany jako nowa instrukcja Java / zastrzeżone słowo kluczowe.
Oba te podejścia można uczynić nieco czystszymi, teraz Java ma Lambdy. Możesz zrobić coś takiego
public Yielderable<Integer> oneToFive() {
return yield -> {
for (int i = 1; i < 10; i++) {
if (i == 6) yield.breaking();
yield.returning(i);
}
};
}
Yielderable
? Czy nie powinno tak być Yieldable
? (czasownik to po prostu „ustąpić”, a nie „ustąpić”, „ustąpić” czy cokolwiek)
yield -> { ... }
zepsuje się od JDK 13, ponieważ yield
jest dodawany jako nowa instrukcja Java / zastrzeżone słowo kluczowe.
Wiem, że to bardzo stare pytanie i są dwa sposoby opisane powyżej:
yield
które oczywiście wiążą się z kosztami zasobów.Istnieje jednak inny, trzeci i prawdopodobnie najbardziej naturalny sposób implementacji yield
generatora w Javie, który jest najbliższą implementacją do tego, co kompilatory C # 2.0+ robią dla yield return/break
generacji: lombok-pg . Jest w pełni oparty na automacie stanowym i wymaga ścisłej współpracy z javac
kodem źródłowym AST. Niestety, wydaje się, że obsługa lombok-pg została przerwana (brak aktywności repozytorium przez ponad rok lub dwa), a oryginalny Project Lombok niestety nie ma tej yield
funkcji (ma lepsze IDE, takie jak Eclipse, wsparcie IntelliJ IDEA).
Stream.iterate (seed, seedOperator) .limit (n) .foreach (akcja) to nie to samo, co operator yield, ale może być przydatne napisanie własnych generatorów w ten sposób:
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
}
}
Sugerowałbym również, jeśli już używasz RXJava w swoim projekcie, aby użyć Observable jako „yieldera”. Można go użyć w podobny sposób, jeśli stworzysz własny 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
}
}
Observables można przekształcić w iteratory, dzięki czemu można ich używać nawet w bardziej tradycyjnych pętlach for. Również RXJava zapewnia naprawdę potężne narzędzia, ale jeśli potrzebujesz tylko czegoś prostego, może to byłaby przesada.
Właśnie opublikowano kolejną (MIT licencjonowanych) roztworu tutaj , otwiera się do producenta w oddzielnym wątku oraz określa ograniczonym kolejki pomiędzy producenta i konsumenta, dzięki czemu do buforowania, do kontroli przepływu i równoległe przetwarzanie potokowe od producenta i konsumenta (tak że konsument może pracować nad skonsumowaniem poprzedniego przedmiotu, podczas gdy producent pracuje nad wyprodukowaniem następnego).
Możesz użyć tego anonimowego formularza klasy wewnętrznej:
Iterable<T> iterable = new Producer<T>(queueSize) {
@Override
public void producer() {
produce(someT);
}
};
na przykład:
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);
}
Lub możesz użyć notacji lambda, aby zmniejszyć wartość domyślną:
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);
}