我想知道何时可以有效地使用IntStream.range
。我有三个原因,我不确定IntStream.range
是多么有用。
(请将开头和结尾视为整数。)
如果我想要一个数组[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()
非常慢。
我使用MersenneTwister来重排数组。 (我在线下载了MersenneTwister课程。)我认为有一种方法可以使用MersenneTwister对IntStream
进行随机播放。
我不认为从int
到start
获取end-1
个数字是有用的。我可以使用for(int i = start; i < end; i++)
,这似乎更容易也不慢。
你能告诉我什么时候应该选择IntStream.range
吗?
答案 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
将为您提供一些常用于一系列整数的功能,例如avg
和IntStream
。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实施为Iterator
和stream from that。