在Javascript上使用几个'.filter'调用大数据会对性能有害吗?

Fli*_*lip 5 javascript arrays

我写了这段代码来过滤一系列单词.我为我要过滤的每种类型的单词写了一个过滤函数,并将它们顺序应用到数组中:

  const wordArray = rawArray.filter(removeNonDomainWords)
                            .filter(removeWordsWithDigits)
                            .filter(removeWordsWithInsideNonWordChars)
                            .filter(removeEmptyWords)
                            .filter(removeSearchTerm, term)
                            .map(word => replaceNonWordCharsFromStartAndEnd(word))
Run Code Online (Sandbox Code Playgroud)

如果我没有弄错的话,这段代码将遍历整个数组六次.

编写一个(在我的场景中更复杂,但仍然很容易)过滤器功能,在逻辑上组合过滤器功能以实现相同的结果,是不是更有效率?

我在功能编程的上下文中学习了过滤器,它应该使我的代码更短更快.这就是为什么我可能没有质疑我在写什么,想着'我在做FP,这一定要好'.

谢谢!

Bex*_*Bex 8

好吧,它迭代六次,但不一定在整个初始数组上.每次过滤它都会变小.使用一种过滤方法会更有效,但差异可能不如预期的那么大.

如果您仍想使用此解决方案,则可以首先使用最具选择性(即预期过滤掉的过滤器)来提高性能.这样,以下数组将会更小,迭代次数也会减少.

正如@Redu指出的那样(在评论中)您可以使用||运算符链接过滤器.这将确保您只进行一次迭代.


这背后的原因是Array.prototype.filter返回一个新数组.将其与Java StreamAPI 进行比较,后者返回一个流,因此可以通过调用列表"深度优先".这样做的缺点是你最终需要一个终端操作来"收集"你的结果.

在javascript中

rawArray.filter(x)
Run Code Online (Sandbox Code Playgroud)

迭代rawArray并返回一个新的过滤后的数组 - 然后可以对其进行过滤或按原样使用.它将导致x每个元素的调用rawArray.

在Java中相当于

rawArray.stream().filter(x)
Run Code Online (Sandbox Code Playgroud)

在这一点上实际上什么都不做.不会打电话x.返回值为a Stream,可以在以后使用.它可以进一步过滤,但直到以某种方式收集值 - 通过终端操作 - 才能进行调用.

让我们比较一下javascript

rawArray.filter(x).filter(y).length
Run Code Online (Sandbox Code Playgroud)

到Java

rawArray.stream().filter(x).filter(y).count()
Run Code Online (Sandbox Code Playgroud)

在javascript中,这将首先遍历所有元素rawArray,调用x它们中的每一个,并将结果存储在中间数组中.然后javascript引擎将迭代中间数组的所有元素,调用y每个元素,并将结果存储在第二个中间数组中,然后检查其大小.

在Java中,代码片段将导致VM迭代rawArray首先调用的元素x,如果xtrue,则调用y每个元素,如果仍然true递增计数器.没有中间数组,只有一次迭代数据集.

函数式编程很有意思,如果使用得当,它会创建更少的代码,这些代码不那么复杂,理想情况下甚至可能更容易阅读,但它确实将很多责任移交给框架(或引擎或VM或其他),以及重要的是要认识到,看似相似的代码虽然行为相似,但在不同环境中的表现却大相径庭.