在Javascript中反转数组的最有效方法是什么?

我最近被问到什么是在Javascript中反转数组的最有效的方法。 此刻,我build议使用for循环和摆弄数组,但是然后意识到有一个本地Array.reverse()方法。

为了好奇,任何人都可以帮助我通过展示例子或指向正确的方向来探索这个问题,所以我可以读到这个? 有关如何衡量performance的任何build议也是可怕的。

基于这个设置:

 var array = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]; var length = array.length; 

Array.reverse(); 是第一或第二最慢!

基准在这里: http : //jsperf.com/js-array-reverse-vs-while-loop/5

跨浏览器,交换循环速度更快。 有两种常见的交换algorithm(见维基百科 ),每种都有两种变化。

这两种交换algorithm是临时交换和XOR交换。

这两个变体处理索引计算的方式不同 第一个变体比较当前的左指数和右指数,然后递减数组的右指数。 第二种变化比较当前左指数和长度的一半,然后重新计算每个迭代的正确指数。

你可能会看到或者看不到这两个变化之间的巨大差异。 例如,在Chrome 18中,临时交换和异或交换的第一次变化比第二次变化慢60%以上,但在Opera 12中,临时交换和异或交换的两种变化都具有类似的性能。

临时掉期:

第一个变体:

 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; } 

XOR交换:

第一个变体:

 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; } 

目前,使用解构赋值的algorithm是其中最慢的一个。 它甚至比Array.reverse();Array.reverse(); 。 但是,使用解构赋值和Array.reverse();的algorithmArray.reverse(); 方法是最短的例子,他们看起来最干净。 我希望他们的performance今后会更好。


另外值得一提的是现代浏览器正在提高arrayspushsplice操作的性能。

在Firefox 10中,这种使用arrayspushsplice for循环algorithm可以与临时交换和XOR交换循环algorithm相媲美。

 for (length -= 2; length > -1; length -= 1) { array.push(array[length]); array.splice(length, 1); } 

但是,您应该坚持交换循环algorithm,直到许多其他浏览器匹配或超过其arrayspushsplice性能。

本机方法总是更快。

所以尽可能使用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;)更快var i = ii - 1;for(;i-- !== 0;)

我在Firefox中打开了一个关于缓慢反转性能的Firefox错误 。 来自Mozilla的人看了接受的post中使用的基准,并说这是相当具有误导性的 – 在他们的分析中,原生方法通常更好地反转arrays。 (正如它应当是的样子!)

用简单的方法,你可以使用地图来做到这一点

 let list = [10, 20, 30, 60, 90] let reversedList = list.map((e, i, a)=> a[(a.length -1) -i]) // [90, 60...] 

交换function是最快的。 这是我写的一个反转函数,它与上面提到的交换函数只有一点相似,但是执行速度更快。

 function reverse(array) { var first = null; var last = null; var tmp = null; var length = array.length; for (first = 0, last = length - 1; first < length / 2; first++, last--) { tmp = array[first]; array[first] = array[last]; array[last] = tmp; } } 

你可以在这里find基准http://jsperf.com/js-array-reverse-vs-while-loop/19

由于没有人想出它,并完成扭转数组的方式列表…

 array.sort(function() { return 1; }) 

这是两种方法的两倍,但除此之外,速度非常缓慢。

http://jsperf.com/js-array-reverse-vs-while-loop/53

这是一个java示例http://www.leepoint.net/notes-java/data/arrays/arrays-ex-reverse.html,展示了如何反转数组。; 很容易转换为JavaScript。

我build议使用一些简单的方法来捕获函数调用之前的时间,然后调用函数。 哪个花费最less的时间/时钟周期将是最快的。

如果你想复制一个数组的反转版本并保持原来的状态:

 a = [0,1,2,3,4,5,6,7,8,9]; b = [] for(i=0;i<a.length;i++){ b.push(a.slice(a.length-i-1,a.length-i)[0]) } 

b的输出:

 [ 9, 8, 7, 6, 5, 4, 3, 2, 1, 0] 

下面是另一个例子,用于永久性地修改数组,以反转它的元素:

 var theArray = ['a', 'b', 'c', 'd', 'e', 'f']; function reverseArrayInPlace(array) { for (var i = array.length - 1; i >= 0; i -= 1) { array.push(array[i]); } array.splice(0, array.length / 2); return array; }; reverseArrayInPlace(theArray); console.log(theArray); // -> ["f", "e", "d", "c", "b", "a"] 

这里有一些我发现的技巧。 信用去Codemanx的原始解决scheme

 array.sort(function() { return 1; }) 

在打字稿中,这可以简化为一行

 array.sort(() => 1) 
 var numbers = [1,4,9,13,16]; console.log(numbers.sort(() => 1)); 

另一个build议,类似于上面,但使用拼接代替:

 var myArray=["one","two","three","four","five","six"]; console.log(myArray); for(i=0;i<myArray.length;i++){ myArray.splice(i,0,myArray.pop(myArray[myArray.length-1])); } console.log(myArray); 

这是使用三元运算符反转数组的最有效和最干净的方法。

 function reverse(arr) { return arr.length < 2 ? arr : [arr.pop()].concat(reverse(arr)); } console.log(reverse([4, 3, 3, 1]));