C # 's' verim 'anahtar kelimesine eşdeğer bir Java var mı?


112

Java'nın kendisinde doğrudan bir eşdeğeri olmadığını biliyorum, ancak belki bir üçüncü taraf?

Gerçekten uygun. Şu anda, bir ağaçtaki tüm düğümleri veren bir yineleyici uygulamak istiyorum, bu verim ile yaklaşık beş satır koddur.


6
Biliyorum biliyorum. Ama daha fazla dil bilmenin daha güçlü olduğunu düşünüyorum. Dahası, şu anda çalıştığım şirkette arka uç geliştirme (yaptığım) Java'da yapılıyor, bu yüzden dili gerçekten
seçemiyorum

Yanıtlar:


91

Bildiğim iki seçenek, Aviad Ben Dov'un 2007'deki bilgi toplama kitaplığı ve 2008'deki Jim Blackler'in YieldAdapter kitaplığıdır (diğer yanıtta da bahsedilmektedir).

Her ikisi de yield returnJava'da -like yapı ile kod yazmanıza izin verecek , böylece her ikisi de isteğinizi karşılayacaktır. İkisi arasındaki önemli farklar şunlardır:

mekanik

Aviad'ın kitaplığı bayt kodu manipülasyonunu kullanırken Jim'inki çoklu okuma kullanıyor. İhtiyaçlarınıza bağlı olarak, her birinin kendi avantajları ve dezavantajları olabilir. Muhtemelen Aviad'ın çözümü daha hızlıyken Jim'inki daha taşınabilir (örneğin, Aviad'ın kütüphanesinin Android'de çalışacağını düşünmüyorum).

Arayüz

Aviad'ın kitaplığı daha temiz bir arayüze sahiptir - işte bir örnek:

Iterable<Integer> it = new Yielder<Integer>() {
    @Override protected void yieldNextCore() {
        for (int i = 0; i < 10; i++) {
            yieldReturn(i);
            if (i == 5) yieldBreak();
        }
    }
};

Jim yol daha karmaşık olsa da, sizi gerektiren adeptjenerik Collectorbir olan collect(ResultHandler)öf ... yöntem. Bununla birlikte, Jim'in kodunun etrafında Zoom Information tarafından sağlanan bu sarmalayıcı gibi bir şey kullanabilirsiniz, bu da bunu büyük ölçüde basitleştirir:

Iterable<Integer> it = new Generator<Integer>() {
    @Override protected void run() {
        for (int i = 0; i < 10; i++) {
            yield(i);
            if (i == 5) return;
        }
    }
};

Lisans

Aviad'ın çözümü BSD'dir.

Jim'in çözümü kamu malıdır ve yukarıda bahsedilen sarmalayıcı da öyle.


3
Harika cevap. Sadece soruyu tam olarak cevaplamakla kalmadınız, bunu çok net bir şekilde yaptınız. Ayrıca, yanıtınızın biçimini ve lisans bilgilerini nasıl dahil ettiğinizi de beğendim. Harika cevaplara devam edin! :)
Malcolm

1
Guava'yı unutma AbstractIterator.
shmosel

Burada yapımcı için ayrı bir iş parçacığı başlatan ve üretici ile tüketici arasında sınırlı bir sıra oluşturan başka bir (MIT lisanslı) çözüm yayınladım: github.com/lukehutch/Producer
Luke Hutchison

Yeni bir Java ifadesi / ayrılmış anahtar kelime olarak eklendiğinden yield(i), JDK 13'ten itibaren bozulacağını unutmayın yield.
Luke Hutchison

14

Bu yaklaşımların her ikisi de biraz daha temiz hale getirilebilir şimdi Java'da Lambdas var. Gibi bir şey yapabilirsin

public Yielderable<Integer> oneToFive() {
    return yield -> {
        for (int i = 1; i < 10; i++) {
            if (i == 6) yield.breaking();
            yield.returning(i);
        }
    };
}

Burada biraz daha açıkladım.


4
Yielderable? Sadece olması gerekmiyor Yieldablemu? (fiil sadece 'verim' olur, 'veren' veya 'verim' ya da her neyse)
Jochem Kuijpers

yield -> { ... }yieldYeni bir Java ifadesi / ayrılmış anahtar kelime olarak eklendiğinden , JDK 13 itibarıyla bozulacaktır .
Luke Hutchison

1

Bunun çok eski bir soru olduğunu biliyorum ve yukarıda açıklanan iki yol var:

  • bayt kodu manipülasyonu taşıma sırasında o kadar kolay değil;
  • iş parçacığı temelli yield, açıkça kaynak maliyetleri var.

Bununla birlikte, oluşturucuyu yieldJava'da uygulamanın başka, üçüncü ve muhtemelen en doğal yolu C # 2.0+ derleyicilerin yield return/breaknesil için yaptıklarına en yakın uygulama olan lombok-pg . Tamamen bir durum makinesine dayalıdır ve javacAST kaynak kodunu işlemek için sıkı bir işbirliği gerektirir . Ne yazık ki, lombok-pg desteği kesilmiş gibi görünüyor (bir veya iki yıldan fazla bir depo aktivitesi yok) ve orijinal Project Lombok maalesef bu yieldözellikten yoksun (Eclipse, IntelliJ IDEA desteği gibi daha iyi IDE'ye sahip olsa da).


1

Stream.iterate (seed, seedOperator) .limit (n) .foreach (action) , verim operatörü ile aynı değildir, ancak kendi oluşturucularınızı şu şekilde yazmak yararlı olabilir:

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
    }
}

0

Ayrıca , projenizde zaten RXJava kullanıyorsanız, bir Observable'ı "sağlayıcı" olarak kullanmak için öneriyorum . Kendi Gözlemlenebilirinizi yaparsanız benzer bir şekilde kullanılabilir.

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
    }
}

Gözlenebilirler yineleyicilere dönüştürülebilir, böylece onları daha geleneksel döngülerde bile kullanabilirsiniz. Ayrıca RXJava size gerçekten güçlü araçlar sunar, ancak yalnızca basit bir şeye ihtiyacınız varsa, o zaman belki bu bir abartı olur.


0

Burada , üreticiyi ayrı bir iş parçacığında başlatan ve üretici ile tüketici arasında sınırlandırılmış bir kuyruk oluşturan ve üretici ile tüketici arasında arabelleğe alma, akış kontrolü ve paralel boru hattına izin veren başka bir (MIT lisanslı) çözüm yayınladım (yani Üretici bir sonraki ürünü üretmeye çalışırken tüketici önceki ürünü tüketmeye çalışıyor olabilir).

Bu anonim iç sınıf formunu kullanabilirsiniz:

Iterable<T> iterable = new Producer<T>(queueSize) {
    @Override
    public void producer() {
        produce(someT);
    }
};

Örneğin:

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);
}

Veya standart metinde kısmak için lambda gösterimini kullanabilirsiniz:

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);
}
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.