Core Java

Java yield-like using Stream API

Several programming languages, such as Ruby or Python to name a few, provides the yield command. Yield provides an effective way, in terms of memory consumption, to create series of values, by generating such values on demand. More information on Python Yield.

Let’s consider a class or method requiring a huge amount of secure random integers. The classical approach would be to create an array or collection of such integers. Yield provides two major advantages over such approach:
 
 
 

  • yield does not require to know the length of the series in advance.
  • yield does not require to store all values in memory.

Fortunately, yield features can be used in Java 8 thanks to Stream API:

import java.security.NoSuchAlgorithmException;
import java.security.SecureRandom;
import java.util.Date;
import java.util.function.Supplier;
import java.util.stream.Stream;

public class Yield {

	private static final Integer RANDOM_INTS = 10;

	public static void main(String[] args) {

		try (Stream randomInt = generateRandomIntStream()){
			Object[] randomInts = randomInt.limit(RANDOM_INTS)
                                .sorted().toArray();
			for (int i = 0; i < randomInts.length;i++)
				System.out.println(randomInts[i]);
		} catch (NoSuchAlgorithmException e) {
			e.printStackTrace();
		}
	}

	private static Stream generateRandomIntStream()
           throws NoSuchAlgorithmException{
		return Stream.generate(new Supplier() {

			final SecureRandom random = SecureRandom
                                .getInstance("SHA1PRNG");
			boolean init = false;
			int numGenerated = 0;

			@Override
			public Integer get() {
				if (!init){
					random.setSeed(new Date().getTime());
					init = true;
					System.out.println("Seeding");
				}
				final int nextInt = random.nextInt();
				System.out.println("Generated random "
                                         + numGenerated++
                                         + ": " + nextInt);
				return nextInt;
			}

		});
	}

}

Following is the output after provided code snippet is executed:

Seeding
Generated random 0: -896358073
Generated random 1: -1268521873
Generated random 2: 9627917
Generated random 3: -2106415441
Generated random 4: 935583477
Generated random 5: -1132421439
Generated random 6: -1324474601
Generated random 7: -1768257192
Generated random 8: -566921081
Generated random 9: 425501046
-2106415441
-1768257192
-1324474601
-1268521873
-1132421439
-896358073
-566921081
9627917
425501046
935583477

It is easy to see that Supplier is only instantiated one. Of course, we can take advantage of all Stream API features such as limit() and sorted().

The line randomInt.limit(RANDOM_INTS).sorted().toArray() triggers the generation of RANDOM_INTS values which are then sorted and stored as an array.

Reference: Java yield-like using Stream API from our JCG partner Sergio Molina at the TODOdev blog.
Subscribe
Notify of
guest

This site uses Akismet to reduce spam. Learn how your comment data is processed.

4 Comments
Oldest
Newest Most Voted
Inline Feedbacks
View all comments
Yannick Majoros
Yannick Majoros
10 years ago

Cool article.

I somewhat played with your code, and was able to reduce the main part to this:

SecureRandom random = SecureRandom.getInstance(“SHA1PRNG”);
Integer[] randomInts = Stream.generate(random::nextInt).limit(RANDOM_INTS).sorted().toArray(size -> new Integer[size]);

As a side note, a more functionalish ™ way of printing the array could be:

Arrays.stream(randomInts).forEach(System.out::println);

Now, I’m not sure this is like “using yield in java”. It’s just the way lambdas are implemented in Java, and it’s somewhat different to other languages. What do you think?

Sergio M
10 years ago

Hi Yannick

Thanks for your feedback. You are right regarding lambda against yield implementation in other languages. However, the point was to show a somehow close approach for people used to develop using yield. Ie, generators being called as required instead of prior generation of data (without prior knowledge of how many items will be required or memory allocation of such data).

From my point of view, Java lambda implementation is easier to understand than yield implementation of python or ruby.

Yannick Majoros
Yannick Majoros
10 years ago

Hi Sergio, Well, I think the point in understanding java lambdas’ approach (which I’m still learning too), is that they are in essence “throw-away” sources. In my opinion, starting from your example but depending on the end usage that will be made from those generated ints, all that is strictly needed is this: Stream.generate(random::nextInt) The rest of the code is probably unnecessary, and could even reverse the goal of generating stuff only when it’s needed. No need for collections or arrays if it shouldn’t be stored anyway. For example, if all you want to do is print 10 ints, you… Read more »

Yannick Majoros
Yannick Majoros
10 years ago

(any way to format code better in comments?)

Back to top button