我什么时候应该在Java中使用IntStream.range?

Nic*_*kel 34 java java-8 java-stream

我想知道何时可以IntStream.range有效使用.我有三个原因,我不确定它有多大用处IntStream.range.

(请将开头和结尾视为整数.)

  1. 如果我想要一个数组,[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;
    
    Run Code Online (Sandbox Code Playgroud)

    这可能是因为toArray()在IntStream.range(start, end).toArray()非常缓慢.

  2. 我使用MersenneTwister来重排数组.(我在网上下载了MersenneTwister课程.)我认为没有办法IntStream使用MersenneTwister 进行随机播放.

  3. 我不认为刚开int从数字start到end-1是很有用的.我可以使用for(int i = start; i < end; i++),这似乎更容易,而不是慢.

你能告诉我什么时候应该选择IntStream.range吗?

Stu*_*rks 26

有几种用途IntStream.range.

一个是使用int值本身:

IntStream.range(start, end).filter(i -> isPrime(i))....
Run Code Online (Sandbox Code Playgroud)

另一个是做N次:

IntStream.range(0, N).forEach(this::doSomething);
Run Code Online (Sandbox Code Playgroud)

你的情况(1)是创建一个充满范围的数组:

int[] arr = IntStream.range(start, end).toArray();
Run Code Online (Sandbox Code Playgroud)

你说这是"非常慢"但是,和其他受访者一样,我怀疑你的基准测试方法.对于小型阵列,流设置确实有更多的开销,但这应该是如此之小以至于不可察觉.对于大型阵列,开销应该可以忽略不计,因为填充大型阵列主要是内存带宽.

有时您需要填充现有数组.你可以这样做:

int[] arr = new int[end - start];
IntStream.range(0, end - start).forEach(i -> arr[i] = i + start);
Run Code Online (Sandbox Code Playgroud)

有一种实用方法Arrays.setAll可以更简洁地做到这一点:

int[] arr = new int[end - start];
Arrays.setAll(arr, i -> i + start);
Run Code Online (Sandbox Code Playgroud)

还Arrays.parallelSetAll可以并行填充现有阵列.在内部,它只是使用IntStream并调用parallel()它.这应该为多核系统上的大阵列提供加速.

我发现我在Stack Overflow上的相当多的答案都涉及到使用IntStream.range.您可以在搜索框中使用以下搜索条件搜索它们:

user:1441122 IntStream.range
Run Code Online (Sandbox Code Playgroud)

IntStream.range我觉得特别有用的一个应用是对数组的元素进行操作,其中数组索引以及数组的值参与计算.有这样一类问题.

例如,假设您要查找数组中不断增加的数字运行的位置.结果是第一个数组中的索引数组,其中每个索引指向一个运行的开始.

要计算此值,请观察运行从值小于先前值的位置开始.(运行也从位置0开始).从而:

    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]
Run Code Online (Sandbox Code Playgroud)

当然,您可以使用for循环执行此操作,但我发现IntStream在许多情况下使用是更可取的.例如,很容易将未知数量的结果存储到数组中toArray(),而使用for循环则必须处理复制和调整大小,这会分散循环的核心逻辑.

最后,IntStream.range并行运行计算要容易得多.

  • 为什么在第三个例子中使用`forEach`写入预先分配的数组而不是使用干净的`int [] arr = IntStream.range(start,end).toArray();`? (3认同)
  • @Holger有时你想填充一个预先存在的数组.OP声称`IntStream.range(开始,结束).toArray()`太慢了,所以他清楚地知道这一点.但无论如何我应该澄清背景.另外,只有`Arrays.parallelSetAll`使用`IntStream`所以我也会调整它. (2认同)
  • @MyStackRunnethOver 是的,缺少 arg,谢谢。固定的。 (2认同)

biz*_*lop 6

这是一个例子:

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();
    }

}
Run Code Online (Sandbox Code Playgroud)

那么,让我们看看sum()它是做什么的:它计算任意数字流的总和.我们用两种不同的方式来称呼它:一次是明确的数字列表,一次是一个范围.

如果你只有call A,你可能会想把这两个数字放到一个数组中并传递给它,sum()但这显然不是一个选项call B(你的内存不足).同样,你可以通过开始和结束call B,但是你不能支持的情况call A.

总而言之,范围在这里很有用,因为:

  • 我们需要在方法之间传递它们
  • 目标方法不仅适用于范围,还适用于任何数字流
  • 但它只对流的各个数字进行操作,并按顺序读取它们.(这就是为什么一般来说,改变流是一个糟糕的想法.)

还有可读性参数:使用流的代码可以比循环更简洁,因此更具可读性,但我想展示一个示例,其中依赖于IntStreans 的解决方案在功能上也更优越.

我曾经LongStream强调过这一点,但同样如此IntStream

是的,对于简单的求和,这可能看起来有点过分,但考虑例如水库采样

  • 很公平,我只是捣碎了很多零而不计算:) (2认同)

ton*_*kai 5

IntStream.range将一个整数范围作为流返回,以便您可以对其进行流处理.

喜欢占据每个元素的正方形

IntStream.range(1, 10).map(i -> i * i);  
Run Code Online (Sandbox Code Playgroud)