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

时间:2016-08-17 13:47:49

标签: 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;
    

    这可能是因为toArray()中的IntStream.range(start, end).toArray()非常慢。

  2. 我使用MersenneTwister来重排数组。 (我在线下载了MersenneTwister课程。)我认为有一种方法可以使用MersenneTwister对IntStream进行随机播放。

  3. 我不认为从intstart获取end-1个数字是有用的。我可以使用for(int i = start; i < end; i++),这似乎更容易也不慢。

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

7 个答案:

答案 0 :(得分:21)

IntStream.range有几种用途。

一种是使用int值本身:

IntStream.range(start, end).filter(i -> isPrime(i))....

另一个是做N次的事情:

IntStream.range(0, N).forEach(this::doSomething);

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

int[] arr = IntStream.range(start, end).toArray();

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

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

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

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

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

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

我发现有很多关于Stack Overflow的答案涉及使用IntStream.range。您可以在搜索框中使用以下搜索条件搜索它们:

user:1441122 IntStream.range

我觉得特别有用的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]

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

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

答案 1 :(得分: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();
    }

}

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

如果您只有call A,则可能会将这两个数字放入数组并将其传递给sum(),但这显然不是call B的选项(您可以内存耗尽)。同样,你可以通过call B的开头和结尾,但是你不能支持call A的情况。

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

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

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

我使用LongStream强调了这一点,但同样适用于IntStream

是的,对于简单的求和,这可能看起来有点过分,但请考虑例如reservoir sampling

答案 2 :(得分:5)

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

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

IntStream.range(1, 10).map(i -> i * i);  

答案 3 :(得分:2)

完全取决于用例。但是,语法和流API增加了许多简单的一个衬里,它们肯定可以取代传统的循环。

在某些情况下,

IntStream非常有用且语法糖

IntStream.range(1, 101).sum();
IntStream.range(1, 101).average();
IntStream.range(1, 101).filter(i -> i % 2 == 0).count();
//... and so on

无论您使用IntStream做什么,都可以使用传统循环。因为一个衬里更精确地理解和维护。

对于负循环,我们不能使用IntStream#range,它只能以正增量运行。因此无法遵循,

for(int i = 100; i > 1; i--) {
    // Negative loop
}
  • 案例1:在这种情况下,传统循环要快得多,因为toArray有点开销。

  • 案例2:我对此一无所知,道歉。

  • 案例3: IntStream根本不慢,IntStream.range和传统循环在性能方面差不多相同

见:

答案 4 :(得分:1)

基本上,如果您需要Stream操作,则可以使用range()方法。例如,要使用并发或想要使用map()reduce()。那么你最好使用IntStream

例如:

IntStream.range(1, 5).parallel().forEach(i -> heavyOperation());

或者:

IntStream.range(1, 5).reduce(1, (x, y) -> x * y)  
// > 24

您也可以使用for循环实现第二个示例,但您需要中间变量等。

另外,如果你想要第一场比赛,你可以使用findFirst()和表兄弟来停止使用Stream的剩余部分

答案 5 :(得分:1)

以下是IntStream循环的传统之间的一些差异:

    延迟评估
  • IntStream,在调用终端操作时遍历管道。 for循环在每次迭代时进行评估。
  • sum将为您提供一些常用于一系列整数的功能,例如avgIntStream
  • IntStream将允许您以一种功能性的方式对一系列int进行多个操作编码,这些操作可以更流畅地读取 - 特别是如果您有大量操作。

因此,当这些差异中的一个或多个对您有用时,基本上使用Stream

但请记住,将Stream声音拖曳得非常奇怪,因为IntSupplier不是数据结构,因此将其改组是不合理的(如果您计划建设的话)一个特殊的 from pandas import DataFrame df = pf.DataFrame({ 'column_name' : [u'Monday,30 December,2013', u'Delivered', u'19:23', u'1']}) )。改为改组结果。

至于性能,虽然可能会有一些开销,但在这两种情况下你仍会迭代N次,并且真的不在乎。

答案 6 :(得分:0)

您可以将{Mersenne Twister实施为Iteratorstream from that