When should I use IntStream.range in Java?

2022-08-31 22:09:25

I would like to know when I can use effectively. I have three reasons why I am not sure how useful is.IntStream.rangeIntStream.range

(Please think of start and end as integers.)

  1. If I want an array, , the code below is much faster.[start, start+1, ..., end-2, end-1]

    int[] arr = new int[end - start];
    int index = 0;
    for(int i = start; i < end; i++)
        arr[index++] = i;
    

    This is probably because in is very slow.toArray()IntStream.range(start, end).toArray()

  2. I use MersenneTwister to shuffle arrays. (I downloaded MersenneTwister class online.) I do not think there is a way to shuffle using MersenneTwister.IntStream

  3. I do not think just getting numbers from to is useful. I can use , which seems easier and not slow.intstartend-1for(int i = start; i < end; i++)

Could you tell me when I should choose ?IntStream.range


答案 1

There are several uses for .IntStream.range

One is to use the values themselves:int

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 that can do this even more concisely:Arrays.setAll

int[] arr = new int[end - start];
Arrays.setAll(arr, i -> i + start);

There is also which can fill an existing array in parallel. Internally, it simply uses an and calls on it. This should provide a speedup for large array on a multicore system.Arrays.parallelSetAllIntStreamparallel()

I've found that a fair number of my answers on Stack Overflow involve using . You can search for them using these search criteria in the search box:IntStream.range

user:1441122 IntStream.range

One application of 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.IntStream.range

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 is preferable in many cases. For example, it's easy to store an unknown number of results into an array using , whereas with a for-loop you have to handle copying and resizing, which distracts from the core logic of the loop.IntStreamtoArray()

Finally, it's much easier to run computations in parallel.IntStream.range


答案 2

Here's an example:

public class Test {

    public static void main(String[] args) {
        System.out.println(sum(LongStream.of(40,2))); // call A
        System.out.println(sum(LongStream.range(1,100_000_000))); //call B
    }

    public static long sum(LongStream in) {
        return in.sum();
    }

}

So, let's look at what does: it counts the sum of an arbitrary stream of numbers. We call it in two different ways: once with an explicit list of numbers, and once with a range.sum()

If you only had , you might be tempted to put the two numbers into an array and pass it to but that's clearly not an option with (you'd run out of memory). Likewise you could just pass the start and end for , but then you couldn't support the case of .call Asum()call Bcall Bcall A

So to sum it up, ranges are useful here because:

  • We need to pass them around between methods
  • The target method doesn't just work on ranges but any stream of numbers
  • But it only operates on individual numbers of the stream, reading them sequentially. (This is why shuffling with streams is a terrible idea in general.)

There is also the readability argument: code using streams can be much more concise than loops, and thus more readable, but I wanted to show an example where a solution relying on s is functionally superior too.IntStrean

I used LongStream to emphasise the point, but the same goes for IntStream

And yes, for simple summing this may look like a bit of an overkill, but consider for example reservoir sampling


推荐