Is there a Java equivalent to C#'s 'yield' keyword?
Asked Answered
K

10

116

I know there is no direct equivalent in Java itself, but perhaps a third party?

It is really convenient. Currently I'd like to implement an iterator that yields all nodes in a tree, which is about five lines of code with yield.

Kobe answered 30/12, 2009 at 16:8 Comment(1)
I know, I know. But I think knowing more languages is more power. Furthermore, the backend development (which I'm doing) in the company I work for right now is being done in Java, so I can't really choose the language :(Kobe
A
95

The two options I know of is Aviad Ben Dov's infomancers-collections library from 2007 and Jim Blackler's YieldAdapter library from 2008 (which is also mentioned in the other answer).

Both will allow you to write code with yield return-like construct in Java, so both will satisfy your request. The notable differences between the two are:

Mechanics

Aviad's library is using bytecode manipulation while Jim's uses multithreading. Depending on your needs, each may have its own advantages and disadvantages. It's likely Aviad's solution is faster, while Jim's is more portable (for example, I don't think Aviad's library will work on Android).

Interface

Aviad's library has a cleaner interface - here's an example:

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

While Jim's is way more complicated, requiring you to adept a generic Collector which has a collect(ResultHandler) method... ugh. However, you could use something like this wrapper around Jim's code by Zoom Information which greatly simplifies that:

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

License

Aviad's solution is BSD.

Jim's solution is public domain, and so is its wrapper mentioned above.

Almatadema answered 18/6, 2013 at 10:59 Comment(4)
Fantastic answer. Not only did you totally answer the question, you did so in a very clear manner. Also, I like your answer's format and how you included license information. Keep up the awesome answering! :)Whiffletree
Don't forget Guava's AbstractIterator.Event
I just published another (MIT-licensed) solution here, which launches a separate thread for the producer, and sets up a bounded queue between the producer and the consumer: github.com/lukehutch/ProducerDistend
When I try to use "Jim's code by Zoom Information" I have an error "Yield outside of switch expression". Something has changed? I'm using Java 21.Measure
R
14

Both of these approaches can be made a bit cleaner now Java has Lambdas. You can do something like

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

I explained a bit more here.

Rounce answered 21/3, 2015 at 19:47 Comment(3)
Yielderable? Shouldn't it just be Yieldable? (the verb being just 'yield', not 'yielder' or 'yielderate' or whatever)Lilienthal
yield -> { ... } will break as of JDK 13, since yield is being added as a new Java statement / reserved keyword.Distend
@LukeHutchison no it doesn’t. Only invocations of a method named “yield” require a qualifying expression or type to be distinguishable from the yield statement. Naming a variable yield and using it, does not require any changes.Murtagh
S
2

I know it's a very old question here, and there are two ways described above:

  • bytecode manipulation that's not that easy while porting;
  • thread-based yield that obviously has resource costs.

However, there is another, the third and probably the most natural, way of implementing the yield generator in Java that is the closest implementation to what C# 2.0+ compilers do for yield return/break generation: lombok-pg. It's fully based on a state machine, and requires tight cooperation with javac to manipulate the source code AST. Unfortunately, the lombok-pg support seems to be discontinued (no repository activity for more than a year or two), and the original Project Lombok unfortunately lacks the yield feature (it has better IDE like Eclipse, IntelliJ IDEA support, though).

Sonjasonnet answered 11/3, 2014 at 8:33 Comment(1)
When I try to use this ecample I have an error "Yield outside of switch expression". Something has changed? I'm using Java 21.Measure
D
2

I just published another (MIT-licensed) solution here, which launches the producer in a separate thread, and sets up a bounded queue between the producer and the consumer, allowing for buffering, flow control, and parallel pipelining between producer and consumer (so that the consumer can be working on consuming the previous item while the producer is working on producing the next item).

You can use this anonymous inner class form:

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

for example:

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

Or you can use lambda notation to cut down on 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);
}
Distend answered 28/10, 2019 at 10:34 Comment(0)
C
1

Stream.iterate(seed, seedOperator).limit(n).foreach(action) is not the same as yield operator, but it may be usefull to write your own generators this way:

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
    }
}
Cassock answered 29/3, 2017 at 20:45 Comment(0)
P
1

I'd also suggest if you're already using RXJava in your project to use an Observable as a "yielder". It can be used in a similar fashion if you make your own 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 can be transformed into iterators so you can even use them in more traditional for loops. Also RXJava gives you really powerful tools, but if you only need something simple then maybe this would be an overkill.

Passageway answered 7/4, 2019 at 18:8 Comment(0)
T
0
// Java code for Stream.generate()
// to generate an infinite sequential
// unordered stream
import java.util.*;
import java.util.stream.Stream;
  
class GFG {
      
    // Driver code
    public static void main(String[] args) {
      
    // using Stream.generate() method 
    // to generate 5 random Integer values
    Stream.generate(new Random()::nextInt)
      .limit(5).forEach(System.out::println); 
    }
}

From here.

Tattletale answered 7/6, 2022 at 17:49 Comment(0)
C
0

I wrote a new library that has implemented generator for Java. It's simple, thread-free and fast.

Here is an example of generating endless fibonacci numbers:

public static Seq<Integer> fibonacci() {
    return c -> {
        int a = 1;
        int b = 1;
        c.accept(a);
        c.accept(b);
        while (true) {
            c.accept(b = a + (a = b));
        }
    };
}

The Seq interface is just like Java Stream and Kotlin Sequence, but faster than all of them.

Here, let's print the first 7 elements of the fibonacci series

Seq<Integer> fib = fibonacci();
fib.take(7).printAll(","); // => 1,1,2,3,5,8,13

For the original problem, yielding all nodes of a tree? One line is enough.

Seq<Node> seq = Seq.ofTree(root, n -> Seq.of(n.left, n.right));
Catalyst answered 14/12, 2022 at 5:45 Comment(0)
C
0

Yes, you can have yield return like syntax in Java!

In java, to gain syntax like:

Yielder(list).forEach(x->doSomething(x))

One creates a class like this:

public class Yielder {
    public List<String> l;
    
    public Yielder(List<String> l){
       this.l=l;
    }

    public void forEach(Consumer<String> f){
        for(String s : l){
            f.accept(s);
        }
    }
}

Where f.accept() fulfills the role of yield return.

Cockle answered 13/3, 2023 at 15:59 Comment(0)
S
0

Here is how to convert code with yield logic to Java equivalent. It might require some effort but at least there is no need of a third party library (still, it require support for lambdas):

C# :

public IEnumerable<int> Foo()
{
     for (int i = 0; i < 5; i++)
     { 
          yield return i;
     }
}

Java :

public static Iterable<Integer> foo() {
    return () -> new Iterator<>() {
        Integer counter = 0;

        @Override
        public boolean hasNext() {
            return counter < 5;
        }
        
        @Override
        public Integer next() {
            return counter++;
        }
    };
}
Sprang answered 8/12, 2023 at 10:7 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.