Java 如何对使用JDK streams API的代码进行渐近分析?
一般来说,我知道我们必须查看源代码才能了解代码的性能 但更具体地说,这段代码在一个有竞争力的编程网站上超时 这将查找流中从Java 如何对使用JDK streams API的代码进行渐近分析?,java,java-stream,complexity-theory,Java,Java Stream,Complexity Theory,一般来说,我知道我们必须查看源代码才能了解代码的性能 但更具体地说,这段代码在一个有竞争力的编程网站上超时 这将查找流中从0到100的数字出现频率。 数组中的数字介于0和100之间 // Times out with int[] array containing 100000 elements. List<Integer> l = new ArrayList<>(); for( int i = 0 ; i < array.length ;
0
到100
的数字出现频率。
数组中的数字介于0
和100
之间
// Times out with int[] array containing 100000 elements.
List<Integer> l = new ArrayList<>();
for( int i = 0 ; i < array.length ; i ++){
l.add(array[i]);
}
int[] counts = new int[100];
Arrays.stream(array).forEach( i -> counts[i] = Collections.frequency( l, i));
//包含100000个元素的int[]数组超时。
列表l=新的ArrayList();
for(int i=0;icounts[i]=Collections.frequency(l,i));
这段代码的Big-O分析是什么?我认为罪魁祸首是我使用Streams API的方式。您正在迭代
数组(大小100000)的所有元素,而您所需要做的就是在您创建的列表中查找数字0到100(假设为独占)的频率,因此有效地迭代100次,如下所示:
int[] counts = new int[100];
IntStream.range(0,100).forEach(i -> counts[i] = Collections.frequency(l,i));
顺便说一句,如果假设您要遍历整个数组以将其转换为一个列表,一种更简单的方法是计算同一循环中元素的出现次数
int[] counts = new int[100];
for( int i = 0 ; i < array.length ; i ++){
counts[array[i]]++; // same asssumption (array[i] < 100)
}
这段代码的Big-O分析是什么
- 没有理由认为
Arrays.stream()
本身的成本会随着问题的大小而增加
Stream.forEach()。您的特定使用不会缩短迭代,因此没有理由期望更严格的限制
- lambda的复杂性是由
Collections.frequency()
驱动的,它与集合的大小(也是n)成线性比例,因为它必须扫描整个对象
总的来说,这就是O(n2)
这里的浪费是扫描整个集合中的每个数组元素。因为您平均期望每个值出现1000次,所以这是非常昂贵的,并且它随数组元素的数量而扩展。我怀疑您打算只扫描一次count
中的每个位置,但即使这样也会非常浪费。你能想出一种方法一次收集所有的频率计数吗?提示:不要想得太多。您可能在暗示一个简单的for
循环或数组.stream(arr).forEach(i->counts[i]=counts[i]+1)
应该更好,我能知道你为什么认为Array.stream
无法扩展吗?@MohanRadhakrishnan,是的,你在评论中描述的方法就是我所想的。它的效率是O(n),对于这个问题,你不会比这个做得更好。当我说Arrays.stream()
不随问题的大小而扩展时,我的意思是调用该方法需要O(1)
,这与Collection.iterator()的典型实现非常相似。它设置了一个流
对象,但它不必执行任何每个元素的工作。
Arrays.stream(array).forEach(i -> counts[i]++);