Saya tahu tidak ada padanan langsung di Java itu sendiri, tapi mungkin pihak ketiga?
Ini sangat nyaman. Saat ini saya ingin menerapkan iterator yang menghasilkan semua node dalam pohon, yaitu sekitar lima baris kode dengan hasil.
Saya tahu tidak ada padanan langsung di Java itu sendiri, tapi mungkin pihak ketiga?
Ini sangat nyaman. Saat ini saya ingin menerapkan iterator yang menghasilkan semua node dalam pohon, yaitu sekitar lima baris kode dengan hasil.
Jawaban:
Dua opsi yang saya tahu adalah pustaka koleksi infomancers Aviad Ben Dov dari 2007 dan pustaka YieldAdapter Jim Blackler dari 2008 (yang juga disebutkan dalam jawaban lain).
Keduanya akan memungkinkan Anda untuk menulis kode dengan yield return
konstruksi -seperti di Java, jadi keduanya akan memenuhi permintaan Anda. Perbedaan penting antara keduanya adalah:
Perpustakaan Aviad menggunakan manipulasi bytecode sementara Jim menggunakan multithreading. Tergantung pada kebutuhan Anda, masing-masing mungkin memiliki kelebihan dan kekurangannya sendiri. Sepertinya solusi Aviad lebih cepat, sedangkan Jim lebih portabel (misalnya, menurut saya perpustakaan Aviad tidak akan berfungsi di Android).
Perpustakaan Aviad memiliki antarmuka yang lebih bersih - berikut contohnya:
Iterable<Integer> it = new Yielder<Integer>() {
@Override protected void yieldNextCore() {
for (int i = 0; i < 10; i++) {
yieldReturn(i);
if (i == 5) yieldBreak();
}
}
};
Sementara Jim jauh lebih rumit, membutuhkan Anda untuk adept
generik Collector
yang memiliki collect(ResultHandler)
metode ... ugh. Namun, Anda dapat menggunakan sesuatu seperti pembungkus ini di sekitar kode Jim dengan Informasi Zoom yang sangat menyederhanakan itu:
Iterable<Integer> it = new Generator<Integer>() {
@Override protected void run() {
for (int i = 0; i < 10; i++) {
yield(i);
if (i == 5) return;
}
}
};
Solusi Aviad adalah BSD.
Solusi Jim adalah domain publik, begitu juga pembungkusnya yang disebutkan di atas.
AbstractIterator
.
yield(i)
akan berhenti pada JDK 13, karena yield
ditambahkan sebagai pernyataan Java baru / kata kunci yang dipesan.
Kedua pendekatan ini dapat dibuat sedikit lebih bersih sekarang Java memiliki Lambdas. Anda bisa melakukan sesuatu seperti
public Yielderable<Integer> oneToFive() {
return yield -> {
for (int i = 1; i < 10; i++) {
if (i == 6) yield.breaking();
yield.returning(i);
}
};
}
Yielderable
? Bukankah seharusnya begitu Yieldable
? (kata kerjanya hanya 'hasil', bukan 'yielder' atau 'yielderate' atau apa pun)
yield -> { ... }
akan rusak pada JDK 13, karena yield
ditambahkan sebagai pernyataan Java baru / kata kunci yang dipesan.
Saya tahu ini pertanyaan yang sangat lama di sini, dan ada dua cara yang dijelaskan di atas:
yield
yang jelas memiliki biaya sumber daya.Namun, ada cara lain, yang ketiga dan mungkin yang paling alami, untuk mengimplementasikan yield
generator di Java yang merupakan implementasi yang paling dekat dengan apa yang dilakukan oleh compiler C # 2.0+ untuk yield return/break
pembuatan: lombok-pg . Ini sepenuhnya didasarkan pada mesin negara, dan membutuhkan kerja sama yang erat javac
untuk memanipulasi kode sumber AST. Sayangnya, dukungan lombok-pg sepertinya dihentikan (tidak ada aktivitas repositori selama lebih dari satu atau dua tahun), dan Project Lombok yang asli sayangnya tidak memiliki yield
fitur (memiliki IDE yang lebih baik seperti Eclipse, namun dukungan IntelliJ IDEA).
Stream.iterate (seed, seedOperator) .limit (n) .foreach (action) tidak sama dengan operator hasil, tetapi mungkin berguna untuk menulis generator Anda sendiri dengan cara ini:
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
}
}
Saya juga menyarankan jika Anda sudah menggunakan RXJava dalam proyek Anda untuk menggunakan Observable sebagai "yielder". Ini dapat digunakan dengan cara yang sama jika Anda membuat Observable Anda sendiri.
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
}
}
Observable dapat diubah menjadi iterator sehingga Anda bahkan dapat menggunakannya dalam for loop yang lebih tradisional. RXJava juga memberi Anda alat yang sangat kuat, tetapi jika Anda hanya membutuhkan sesuatu yang sederhana maka mungkin ini akan berlebihan.
Saya baru saja menerbitkan solusi lain (berlisensi MIT) di sini , yang meluncurkan produsen di utas terpisah, dan menyiapkan antrean terbatas antara produsen dan konsumen, memungkinkan penyanggaan, kontrol aliran, dan pemipaan paralel antara produsen dan konsumen (jadi bahwa konsumen dapat bekerja untuk memakan barang sebelumnya sementara produsen bekerja untuk memproduksi barang berikutnya).
Anda dapat menggunakan formulir kelas dalam anonim ini:
Iterable<T> iterable = new Producer<T>(queueSize) {
@Override
public void producer() {
produce(someT);
}
};
sebagai contoh:
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);
}
Atau Anda dapat menggunakan notasi lambda untuk mengurangi 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);
}