When should I use IntStream.range in Java?
I would like to know when I can use IntStream.range
effectively. I have three reasons why I am not sure how useful IntStream.range
is.
(Please think of start and end as integers.)
-
If I want an array,
[start, start+1, ..., end-2, end-1]
, the code below is much faster.int[] arr = new int[end - start]; int index = 0; for(int i = start; i < end; i++) arr[index++] = i;
This is probably because
toArray()
inIntStream.range(start, end).toArray()
is very slow. I use MersenneTwister to shuffle arrays. (I downloaded MersenneTwister class online.) I do not think there is a way to shuffle
IntStream
using MersenneTwister.I do not think just getting
int
numbers fromstart
toend-1
is useful. I can usefor(int i = start; i < end; i++)
, which seems easier and not slow.
Could you tell me when I should choose IntStream.range
?
There are several uses for IntStream.range
.
One is to use the int
values themselves:
IntStream.range(start, end).filter(i -> isPrime(i))....
Another is to do something N times:
IntStream.range(0, N).forEach(this::doSomething);
Your case (1) is to create an array filled with a range:
int[] arr = IntStream.range(start, end).toArray();
You say this is "very slow" but, like other respondents, I suspect your benchmark methodology. For small arrays there is indeed more overhead with stream setup, but this should be so small as to be unnoticeable. For large arrays the overhead should be negligible, as filling a large array is dominated by memory bandwidth.
Sometimes you need to fill an existing array. You can do that this way:
int[] arr = new int[end - start];
IntStream.range(0, end - start).forEach(i -> arr[i] = i + start);
There's a utility method Arrays.setAll
that can do this even more concisely:
int[] arr = new int[end - start];
Arrays.setAll(arr, i -> i + start);
There is also Arrays.parallelSetAll
which can fill an existing array in parallel. Internally, it simply uses an IntStream
and calls parallel()
on it. This should provide a speedup for large array on a multicore system.
I've found that a fair number of my answers on Stack Overflow involve using IntStream.range
. You can search for them using these search criteria in the search box:
user:1441122 IntStream.range
One application of IntStream.range
I find particularly useful is to operate on elements of an array, where the array indexes as well as the array's values participate in the computation. There's a whole class of problems like this.
For example, suppose you want to find the locations of increasing runs of numbers within an array. The result is an array of indexes into the first array, where each index points to the start of a run.
To compute this, observe that a run starts at a location where the value is less than the previous value. (A run also starts at location 0). Thus:
int[] arr = { 1, 3, 5, 7, 9, 2, 4, 6, 3, 5, 0 };
int[] runs = IntStream.range(0, arr.length)
.filter(i -> i == 0 || arr[i-1] > arr[i])
.toArray();
System.out.println(Arrays.toString(runs));
[0, 5, 8, 10]
Of course, you could do this with a for-loop, but I find that using IntStream
is preferable in many cases. For example, it's easy to store an unknown number of results into an array using toArray()
, whereas with a for-loop you have to handle copying and resizing, which distracts from the core logic of the loop.
Finally, it's much easier to run IntStream.range
computations in parallel.
IntStream.range returns a range of integers as a stream so you can do stream processing over it.
like taking square of each element
IntStream.range(1, 10).map(i -> i * i);