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. |
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?
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.
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 »
(any way to format code better in comments?)