首页 > 解决方案 > 为什么通过 stream() 调用比使用 if 子句更耗时?

问题描述

为什么是这个方法:

public static int howManyDifferentFields() {
    int difcolor = 0;
    difcolor++;
    if (fields[1] != fields[0]) {
        difcolor++;
    }
    if  (fields[2] != fields[1] && fields[2] != fields[0]) {
        difcolor++;
    }
    if (fields[3] != fields[2] && fields[3] != fields[1] && fields[3] != fields[0]) {
        difcolor++;
    }
    if (fields[4] != fields[3] && fields[4] != fields[2] && fields[4] != fields[1] && fields[4] != fields[0]) {
        difcolor++;
    }
    if (fields[5] != fields[4] && fields[5] != fields[3] && fields[5] != fields[2] && fields[5] != fields[1] && fields[5] != fields[0]) {
        difcolor++;
    }
    if (fields[6] != fields[5] && fields[6] != fields[4] && fields[6] != fields[3] && fields[6] != fields[2] && fields[6] != fields[1] && fields[6] != fields[0]) {
        difcolor++;
    }
    if (fields[7] != fields[6] && fields[7] != fields[5] && fields[7] != fields[4] && fields[7] != fields[3] && fields[7] != fields[2] && fields[7] != fields[1] && fields[7] != fields[0]) {
        difcolor++;
    }
    if (fields[8] != fields[7] && fields[8] != fields[6] && fields[8] != fields[5] && fields[8] != fields[4] && fields[8] != fields[3] && fields[8] != fields[2] && fields[8] != fields[1] && fields[8] != fields[0]) {
        difcolor++;
    }
    return difcolor;
}

比这快得多?

public static int howManyDifferentFields2() {
    return (int) Arrays.stream(fields).distinct().count();
}

我想使用第二种方式,因为它的代码少得多。但这需要更多的时间!当我使用第二种方法而不是第一种方法时,程序需要大约 8 倍的时间才能完成。我能做些什么?我可以以某种方式重写第一种方法以使其有效但代码更少吗?在我看来,第二种方法看起来好多了......

标签: javaarraysperformancedata-structuresjava-stream

解决方案


大多数人认为流很快。但他们不是。有很多代码支持它们,而您遇到的正是隐藏的开销。

在一般情况下,一个普通的 for 循环比非并行流(甚至在元素数量不多时并行流)更快。

虽然流可以生成简洁而强大的代码并且有很多好处,但对于小流来说,性能并不是其中之一。


您的逻辑具有 O(n 2 ) 时间复杂度,但如果 n 固定为 8,您就可以了,您可以将其重写为接近等效的循环:

int difcolor = 8;
for (int i = 1; i < fields.length; i++) {
    for (int j = 0; j < i; j++) {
        if (fields[i] == fields[j]) {
            difcolor--;
            break;
       }
    }
}
return difcolor;

从一个完美的分数开始,然后在第一个匹配上递减提高了效率,因为它通过尽早退出内部循环来减少操作的数量,类似于您使用 short circuiting &&

你也可以使用 Set,它的时间复杂度为 O(n),而且更紧凑,但只有 8 个元素,它可能会比上面的循环慢:

Set<Integer> set = new HashSet<>(); // assuming fields is a int[] 
int difcolor = 0;
for (int field : fields) {
    if (set.add(field)) {
        difcolor++;
    }
}

这与您的流版本类似,但没有流开销。

如果对元素进行了排序,则可以一次通过仅比较邻居来完成。


推荐阅读