简体   繁体   English

遍历 object 或数组是否更快?

[英]Is it faster to iterate / loop over an object or an array?

So I've been doing this ProductSearchPage using React and it has a bunch of filter values that I need to set to filter my product list and show results.所以我一直在使用 React 做这个ProductSearchPage ,它有一堆我需要设置的过滤值来过滤我的产品列表并显示结果。

Up until now, I've been handling my product list as an array (even though I'm fetching it as an object, I'm converting it to an array) and I've been using lots of map , forEach and A LOT of filter loops over those arrays over and over again.到目前为止,我一直将我的产品列表作为一个数组来处理(即使我将它作为一个 object 获取,我正在将它转换为一个数组)并且我一直在使用很多mapforEach和很多filter循环遍历那些 arrays。

  • I'll get a productList , I'll filter based on category我会得到一个productList ,我会根据category进行过滤
  • I'll take the new filteredList and filter based on priceFilters我将采用新的filteredList并根据priceFilters进行过滤
  • I'll take the new filteredList and filter based on ratingFilter我将采用新的filteredList并根据ratingFilter进行过滤
  • And so on for brandFilter , featuresFilters , etc.等等brandFilterfeaturesFilters等。

I began to think that I might be creating a black hole of iterations and that might hurt my performance at some point.我开始认为我可能正在创建一个迭代黑洞,这可能会在某个时候损害我的表现。 I'm doing client side searching and filtering.我正在做客户端搜索和过滤。 We're talking about 2k products maximum.我们最多谈论 2k 种产品。

So I wondered if it would be faster to iterate and filter over an object instead of an array.所以我想知道迭代和过滤 object 而不是数组是否会更快。 I would be deleting properties and creating new objects along the way.我会删除属性并在此过程中创建新对象。

So I did this snippet to test:所以我做了这个片段来测试:

And for my surprise the results were a lot in favor of the array loops.令我惊讶的是,结果非常有利于数组循环。

Looping object with for...in: 0.31ms
Looping array with forEach: 0.08ms
Looping array with filter: 0.10ms
Looping array with map: 0.09ms

QUESTION问题

Is this enough evidence that looping through arrays is faster than looping through objects and I should stick to the forEach , map and filter methods?这是否足以证明循环 arrays 比循环对象更快,我应该坚持使用forEachmapfilter方法?

NOTE: This is really simplified.注意:这真的很简单。 In my real case, each product is an object with some properties (some of them are nested properties).在我的真实案例中,每个产品都是一个 object 具有一些属性(其中一些是嵌套属性)。 So my options are to keep the list as an array of object (like I've been doing so far) or I could keep a big object allProducts with each product as a property of that object. Could this change the results?所以我的选择是将列表保留为 object 的数组(就像我到目前为止所做的那样),或者我可以保留一个大的 object allProducts ,每个产品作为 object 的属性。这会改变结果吗?

 const myObject = {}; const myArray = [] for (let i=0; i<=2000; i++) { myObject['prop'+i] = i; } for (let k=0; k<=2000; k++) { myArray[k] = k; } const t0 = window.performance.now(); for (const key in myObject) { if (myObject[key] % 37 === 0) { //console.log(myObject[key] + ' is a multiple of 37'); } } const t1 = window.performance.now(); console.log('Looping object with for...in: ' + (t1 - t0).toFixed(2) + 'ms'); const t2 = window.performance.now(); myArray.forEach((item) => { if (item % 37 === 0) { //console.log(item + ' is a multiple of 37'); } }); const t3 = window.performance.now(); console.log('Looping array with forEach: ' + (t3 - t2).toFixed(2) + 'ms'); const t4 = window.performance.now(); const newArray = myArray.filter((item) => item % 37 === 0); const t5 = window.performance.now(); console.log('Looping array with filter: ' + (t5 - t4).toFixed(2) + 'ms'); const t6 = window.performance.now(); const newArray2 = myArray.map((item) => item*2); const t7 = window.performance.now(); console.log('Looping array with map: ' + (t7 - t6).toFixed(2) + 'ms');

I would be deleting properties and creating new objects along the way.我会删除属性并在此过程中创建新对象。

These operations will likely take orders of magnitude longer than the time it takes to just perform the loop.这些操作可能比执行循环所需的时间长几个数量级。

Unless of course the way you loop also affects the way you create or delete objects/properties, but I assume we're considering a loop that otherwise does identical instructions.当然,除非你循环的方式也会影响你创建或删除对象/属性的方式,但我假设我们正在考虑一个循环,否则它会执行相同的指令。

In the vast majority of cases it's a tiny part of the performance budget (like 1 millionth), and the wrong place to start if you want to optimize a complex application.在绝大多数情况下,它只是性能预算的一小部分(如百万分之一),如果您想优化复杂的应用程序,则从错误的地方开始。 Just run some profiling tools, get an overview of where the application is spending time, and focus on the slowest parts.只需运行一些分析工具,大致了解应用程序将时间花在哪里,并关注最慢的部分。

Is this enough evidence that looping through arrays is faster than looping through objects and I should stick to the forEach, map and filter methods?这是否足以证明循环 arrays 比循环对象更快,我应该坚持使用 forEach、map 和过滤器方法?

No, because it's a single simplified example.不,因为这是一个简化的例子。 It doesn't tell anything about how big of a chunk of the performance budget it represents.它没有说明它所代表的性能预算有多大。 It's probably also different depending on which JS runtime is used.根据使用的 JS 运行时,它可能也有所不同。 All you can derive from it is that with 2000 iterations it takes at worst 0.31 ms.您可以从中得出的结论是,2000 次迭代最多需要 0.31 毫秒。

I expanded the example a bit by adding a very small amount of extra work inside the loop.我通过在循环中添加非常少量的额外工作来稍微扩展示例 This can then be multiplied to see how fast it starts being more significant than just the loop.然后可以将其相乘以查看它开始比循环更重要的速度有多快。 See the iteration function below.请参阅下面的iteration function。 It internally runs identically for both cases.对于这两种情况,它在内部运行相同。

If the complexity is set to 0 (run extra work 0 times), it performs just like the results posted in the question.如果复杂度设置为 0(运行额外工作 0 次),它的执行就像问题中发布的结果一样。 Array is 2 to 4 times faster.数组快 2 到 4 倍。

However just running this work once, the difference is almost gone (~0.7ms vs ~0.8ms for me).然而,只要运行一次这项工作,差异就几乎消失了(对我来说是 ~0.7ms vs ~0.8ms)。 From 2 times and upwards sometimes array wins, sometimes object, but never by a big margin.从 2 次及以上开始,有时阵列获胜,有时 object,但从来没有大的优势。

So the difference becomes insignificant once you do pretty much anything at all inside the loop.因此,一旦您在循环内执行几乎所有操作,差异就变得微不足道了。

 const myObject = {}; const myArray = [] const iterations = 2000; for (let i = 0; i < iterations; i++) { myObject['prop' + i] = i; myArray[i] = i; } let total = 0; function iteration(a, complexity) { const x = {}; for (let i = 0; i < complexity; i++) { // Do some simple instructions const rand = Math.random(); x[`${a}~${i}`] = rand; total += rand; } return x; } function loopObject(complexity) { const results = []; for (const key in myObject) { results.push(iteration(myObject[key], complexity)); } return results; } function loopArray(complexity) { const results = []; myArray.forEach((item) => { results.push(iteration(item, complexity)) }); return results; } const samples = 10; const decimals = 6; function test(complexity) { console.log(`COMPLEXITY ${complexity} (${samples} samples)`) let arrayTimes = []; let objectTimes = []; for (let i = 0; i < samples; i++) { const tA = performance.now(); const resultArray = loopArray(complexity); arrayTimes.push(performance.now() - tA); const tO = performance.now(); const resultObject = loopObject(complexity); objectTimes.push(performance.now() - tO); } const arraySum = arrayTimes.reduce((p, c) => p + c, 0); const objectSum = objectTimes.reduce((p, c) => p + c, 0); const arrayWins = arraySum < objectSum; console.log( `ARRAY ${arrayWins? ' (winner)': ''} avg: ${(arraySum / samples).toFixed(decimals)} min: ${Math.min(...arrayTimes).toFixed(decimals)} max: ${Math.max(...arrayTimes).toFixed(decimals)}`); console.log( `OBJECT ${?arrayWins: ' (winner)': ''} avg. ${(objectSum / samples):toFixed(decimals)} min. ${Math.min(...objectTimes):toFixed(decimals)} max. ${Math.max(...objectTimes);toFixed(decimals)}`), } const complexities = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 20, 50; 100]. complexities;forEach(test);

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM