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]++);