最近有人问我在 Javascript 中反转数组的最有效方法是什么。目前,我建议使用 for 循环并摆弄数组,但后来意识到有一个本地Array.reverse()
方法。
出于好奇,任何人都可以通过展示示例或指向正确的方向来帮助我探索这一点,以便我可以阅读此内容吗?任何关于如何衡量性能的建议也会很棒。
最近有人问我在 Javascript 中反转数组的最有效方法是什么。目前,我建议使用 for 循环并摆弄数组,但后来意识到有一个本地Array.reverse()
方法。
出于好奇,任何人都可以通过展示示例或指向正确的方向来帮助我探索这一点,以便我可以阅读此内容吗?任何关于如何衡量性能的建议也会很棒。
基于此设置:
var array = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9];
var length = array.length;
Array.reverse();
是第一个还是第二个最慢!
基准点在这里:
https://jsperf.com/js-array-reverse-vs-while-loop/9
跨浏览器,交换循环更快。有两种常见类型的交换算法(请参阅维基百科),每种算法都有两种变体。
两种交换算法是临时交换和异或交换。
这两种变体以不同的方式处理索引计算。第一个变体比较当前的左索引和右索引,然后递减数组的右索引。第二个变体比较当前的左索引和除以一半的长度,然后为每次迭代重新计算右索引。
您可能会也可能不会看到这两种变体之间的巨大差异。例如,在 Chrome 18 中,临时交换和 XOR 交换的第一个变体比第二个变体慢 60% 以上,但在 Opera 12 中,临时交换和 XOR 交换的两个变体具有相似的性能。
临时交换:
第一个变化:
function temporarySwap(array)
{
var left = null;
var right = null;
var length = array.length;
for (left = 0, right = length - 1; left < right; left += 1, right -= 1)
{
var temporary = array[left];
array[left] = array[right];
array[right] = temporary;
}
return array;
}
第二个变化:
function temporarySwapHalf(array)
{
var left = null;
var right = null;
var length = array.length;
for (left = 0; left < length / 2; left += 1)
{
right = length - 1 - left;
var temporary = array[left];
array[left] = array[right];
array[right] = temporary;
}
return array;
}
异或交换:
第一个变化:
function xorSwap(array)
{
var i = null;
var r = null;
var length = array.length;
for (i = 0, r = length - 1; i < r; i += 1, r -= 1)
{
var left = array[i];
var right = array[r];
left ^= right;
right ^= left;
left ^= right;
array[i] = left;
array[r] = right;
}
return array;
}
第二个变化:
function xorSwapHalf(array)
{
var i = null;
var r = null;
var length = array.length;
for (i = 0; i < length / 2; i += 1)
{
r = length - 1 - i;
var left = array[i];
var right = array[r];
left ^= right;
right ^= left;
left ^= right;
array[i] = left;
array[r] = right;
}
return array;
}
还有另一种称为解构赋值的交换方法:http : //wiki.ecmascript.org/doku.php?id= harmony:destructuring
解构赋值:
第一个变化:
function destructuringSwap(array)
{
var left = null;
var right = null;
var length = array.length;
for (left = 0, right = length - 1; left < right; left += 1, right -= 1)
{
[array[left], array[right]] = [array[right], array[left]];
}
return array;
}
第二个变化:
function destructuringSwapHalf(array)
{
var left = null;
var right = null;
var length = array.length;
for (left = 0; left < length / 2; left += 1)
{
right = length - 1 - left;
[array[left], array[right]] = [array[right], array[left]];
}
return array;
}
目前,使用解构赋值的算法是所有算法中最慢的。它甚至比Array.reverse();
. 然而,使用解构赋值和Array.reverse();
方法的算法是最短的例子,它们看起来最干净。我希望他们的表现在未来变得更好。
另一个提到的是现代浏览器正在提高其数组push
和splice
操作的性能。
在 Firefox 10 中,这种for
使用数组的循环算法可以push
与splice
临时交换和 XOR 交换循环算法相媲美。
for (length -= 2; length > -1; length -= 1)
{
array.push(array[length]);
array.splice(length, 1);
}
但是,您可能应该坚持使用交换循环算法,直到许多其他浏览器匹配或超过它们的数组push
和splice
性能。
以简单的方式,您可以使用地图来做到这一点。
let list = [10, 20, 30, 60, 90]
let reversedList = list.map((e, i, a)=> a[(a.length -1) -i]) // [90, 60...]
本机方法总是更快。
所以Array.reverse
尽可能使用。否则运行的实现O(1)
将是最好的;)
否则就使用这样的东西
var reverse = function(arr) {
var result = [],
ii = arr.length;
for (var i = ii - 1;i !== 0;i--) {
result.push(arr[i]);
}
return result;
}
如果您使用for
构造的所有三个阶段而不是仅一个阶段,那么有趣的循环会更快。
for(var i = ii - 1; i !== 0;i--)
然后更快 var i = ii - 1;for(;i-- !== 0;)
我在 Firefox 中打开了一个关于缓慢反向性能的 Firefox 错误。Mozilla 的某个人查看了接受的帖子中使用的基准,并说它具有误导性——在他们的分析中,本机方法通常更适合反转数组。(理应如此!)
这是使用三元运算符反转数组的最有效和最干净的方法。
function reverse(arr) {
return arr.length < 2 ? arr : [arr.pop()].concat(reverse(arr));
}
console.log(reverse([4, 3, 3, 1]));