I have created an randomIntStream by this:
final static PrimitiveIterator.OfInt startValue = new Random().ints(0, 60).iterator();
The documentation says this stream is actually endless.
I want to understand what happens there in the backround.
ints(0,60)
is generating an infinite stream of integers. If this is infinite, why my machine is not leaking any memory?
I wonder, how many numbers are actually really generated and if this implemenentation can cause an error at the point where the stream still ends? Or will this stream constantly filled with new integers on the fly and it really never ends therefore?
And if I already ask this question, what is the best practise right now to generate random numbers nowadays?
We can create an infinite stream of any custom type elements by passing a function of a Supplier interface to a generate() method on a Stream.
An IntStream interface extends the BaseStream interface in Java 8. It is a sequence of primitive int-value elements and a specialized stream for manipulating int values. We can also use the IntStream interface to iterate the elements of a collection in lambda expressions and method references.
What is the difference between both? IntStream is a stream of primitive int values. Stream<Integer> is a stream of Integer objects.
IntStream range() method in Java The range() method in the IntStream class in Java is used to return a sequential ordered IntStream from startInclusive to endExclusive by an incremental step of 1. This includes the startInclusive as well.
The stream is infinite¹ so you can generate as many ints as you want without running out. It does not mean that it keeps generating them when you aren't asking for any.
How many numbers are actually generated depends on the code you write. Every time you retrieve a value from the iterator, a value is generated. None is generated in the background, so there's no "extra" memory being used.
¹ as far as your lifetime is concerned, see Eran's answer
To be exact,
IntStream java.util.Random.ints(int randomNumberOrigin, int randomNumberBound)
returns:
an effectively unlimited stream of pseudorandom int values, each conforming to the given origin (inclusive) and bound (exclusive).
This doesn't mean infinite. Looking at the Javadoc, you'll see an implementation note stating that it actually limits the returned IntStream
to Long.MAX_VALUE
elements:
Implementation Note:
This method is implemented to be equivalent to ints(Long.MAX_VALUE, randomNumberOrigin, randomNumberBound).
Of course Long.MAX_VALUE
is a very large number, and therefore the returned IntStream
can be seen as "effectively" without limit. For example, if you consume 1000000 int
s of that stream every second, it will take you about 292471 years to run out of elements.
That said, as mentioned by the other answers, that IntStream
only generates as many numbers as are required by its consumer (i.e. the terminal operation that consumes the int
s).
Streams do not (in general1) store all of their elements in any kind of a data structure:
No storage. A stream is not a data structure that stores elements; instead, it conveys elements from a source such as a data structure, an array, a generator function, or an I/O channel, through a pipeline of computational operations.
Instead, each stream element is computed one-by-one each time the stream advances. In your example, each random int
would actually be computed when you invoke startValue.nextInt()
.
So when we do e.g. new Random().ints(0,60)
, the fact that the stream is effectively infinite isn't a problem, because no random int
s are actually computed until we perform some action that traverses the stream. Once we do traverse the stream, int
s are only computed when we request them.
Here's a small example using Stream.generate
(also an infinite stream) which shows this order of operations:
Stream.generate(() -> {
System.out.println("generating...");
return "hello!";
})
.limit(3)
.forEach(elem -> {
System.out.println(elem);
});
The output of that code is:
generating...
hello!
generating...
hello!
generating...
hello!
Notice that our generator supplier is called once just before every call of our forEach
consumer, and no more. If we didn't use limit(3)
, the program could run forever, but it wouldn't run out of memory.
If we did new Random().ints(0,60).forEach(...)
, it would work the same way. The stream would do random.nextInt(60)
once before every call to the forEach
consumer. The elements wouldn't be accumulated anywhere unless we used some action that required it, such as distinct()
or a collector instead of forEach
.
Stream.Builder
will require a data structure to put their elements in.As said by @Kayaman in his answer. The stream is infinite in the way that numbers can be generated forever. The point lies in the word can. It does only generate numbers if you really request them. It will not just generate X amount of numbers and then stores them somewhere (unless you tell it to do so).
So if you want to generate n (where n is an integer) random numbers. You can just call the overload of ints(0, 60)
, ints(n, 0, 60)
on the stream returned by Random#ints()
:
new Random().ints(n, 0, 60)
Above will still not generate n random numbers, because it is an IntStream
which is lazily executed. So when not using a terminal operation (e.g. collect()
or forEach()
) nothing really happens.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With