最近有人问我在Javascript中反转数组的最有效方法是什么。目前,我建议使用for循环并摆弄数组,但后来意识到有一个原生的Array.reverse()
方法。
出于好奇心的缘故,任何人都可以通过展示示例或指向正确的方向帮助我探索这个问题,以便我可以读到这个吗?关于如何衡量绩效的任何建议也都很棒。
答案 0 :(得分:70)
基于此设置:
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
在浏览器中,交换循环更快。有两种常见类型的交换算法(参见Wikipedia),每种算法都有两种变体。
两种类型的交换算法是临时交换和XOR交换。
这两种变体处理索引计算的方式不同。第一个变体比较当前左索引和右索引,然后递减数组的右索引。第二个变体将当前左侧索引和长度除以一半进行比较,然后重新计算每次迭代的右侧索引。
您可能会或可能不会看到两种变体之间存在巨大差异。例如,在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;
}
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;
}
现在,使用解构赋值的算法是最慢的算法。它甚至比Array.reverse();
慢。但是,使用解构赋值和Array.reverse();
方法的算法是最简单的例子,它们看起来最干净。我希望将来他们的表现会好转。
另一个提及是,现代浏览器正在提高其阵列push
和splice
操作的性能。
在Firefox 10中,使用数组for
和push
的{{1}}循环算法可以与临时交换和XOR交换循环算法相媲美。
splice
但是,您应该坚持使用交换循环算法,直到许多其他浏览器匹配或超过其数组for (length -= 2; length > -1; length -= 1)
{
array.push(array[length]);
array.splice(length, 1);
}
和push
性能。
答案 1 :(得分:17)
本机方法总是更快。
所以尽可能使用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;)
答案 2 :(得分:11)
您可以使用地图以简单的方式完成此操作。
let list = [10, 20, 30, 60, 90]
let reversedList = list.map((e, i, a)=> a[(a.length -1) -i]) // [90, 60...]
答案 3 :(得分:10)
我opened a Firefox bug关于Firefox中的慢速反向性能。 Mozilla的某个人看过接受的帖子中使用的基准测试,并说这是非常误导的 - 在他们的分析中,本机方法通常对于反转数组更好。 (应该是!)
答案 4 :(得分:5)
由于没有人提出它并完成反转数组的方法列表......
array.sort(function() {
return 1;
})
它的速度是两次接近的速度的两倍,但除此之外,速度非常慢。
答案 5 :(得分:3)
交换功能是最快的。这是我写的一个反向函数,它与上面提到的交换函数略有相似,但执行速度更快。
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;
}
}
找到基准测试
答案 6 :(得分:3)
这是使用三元运算符反转数组的最有效和最简洁的方法。
function reverse(arr) {
return arr.length < 2 ? arr : [arr.pop()].concat(reverse(arr));
}
console.log(reverse([4, 3, 3, 1]));
答案 7 :(得分:2)
这是一个示例如何反转数组的java示例http://www.leepoint.net/notes-java/data/arrays/arrays-ex-reverse.html。很容易转换为javascript。
我建议在调用函数之前和函数调用之后使用简单捕获时间的东西。哪个时间/时钟周期最短是最快的。
答案 8 :(得分:1)
以下是我发现的一些技巧。感谢Codemanx的原始解决方案
array.sort(function() { return 1; })
在Typescript中,这可以简化为一行
array.sort(() => 1)
var numbers = [1,4,9,13,16];
console.log(numbers.sort(() => 1));
&#13;
由于这将是JavaScript的未来,我想我会分享它。
如果您的数组只有2个元素
,那么这是另一个技巧array.push(array.shift());
答案 9 :(得分:1)
这是另一个永久修改反转它的元素的数组的例子:
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"]
答案 10 :(得分:1)
如果要复制数组的反转版本并保持原始格式:
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]
答案 11 :(得分:0)
我找到了一个简单的方法来执行此操作.slice()。reverse()
var yourArray = ["first", "second", "third", "...", "etc"]
var reverseArray = yourArray.slice().reverse()
console.log(reverseArray)
你会得到
["etc", "...", "third", "second", "first"]
答案 12 :(得分:0)
您还可以使用reduceRight
来遍历数组的每个值(从右到左)
const myArray = [1, 2, 3, 4, 5]
const reversedArray = myArray.reduceRight((acc, curr) => [...acc, curr], [])
console.log(reversedArray) // [5, 4, 3, 2, 1]
答案 13 :(得分:0)
将以下内容粘贴到浏览器或node.js上的任何JavaScript运行时控制台中,将对大量数字进行直接基准测试。
以我的情况说40,000
var array = Array.from({ length: 40000 }, () =>
Math.floor(Math.random() * 40000)
);
var beforeStart = Date.now();
var reversedArray = array.map((obj, index, passedArray) => {
return passedArray[passedArray.length - index - 1];
});
console.log(reversedArray);
var afterCustom = Date.now();
console.log(array.reverse());
var afterNative = Date.now();
console.log(`custom took : ${afterCustom - beforeStart}ms`);
console.log(`native took : ${afterNative - afterCustom}ms`);
您只需直接运行该代码段即可查看自定义版本的价格。
答案 14 :(得分:0)
为了完整起见,还应该从更广泛的角度考虑效率,而不仅仅是执行时性能。
因此,我想添加一个交换函数,该函数比accepted answer中的交换函数要慢得多(请使用perf工具自行测试),但它还具有以下其他有益特性:
function fastSwapReverse (array) {
// half length for odd arrays is added by 1
var len = array.length;
var half = len % 2 === 0 ? len / 2 : Math.ceil(len / 2)
var k = 0, A = new Array(len)
// from 0 to half swap the elements at
// start: 0 + i
// end: len - i -1
for (k = 0; k < half; k++) {
A[k] = array[len - k - 1];
A[len - k - 1] = array[k];
}
return A;
}
尽管原始的Array.prototype.reverse
方法也确实改变了数组
答案 15 :(得分:0)
只需使用for loop
从背面开始复制数组并返回该新数组,这是一种更有效的显示方式,也是O(n)表示法哪种更好!
var array1 = ["yes", "no", "maybe", "always", "sometimes", "never", "if"];
var array2 = [5,8,2,9,5,6,3,1];
function reverseArray(arr) {
var newArray = [];
for (var x = arr.length - 1; 0 <= x; --x) {
newArray.push(arr[x]);
}
return newArray;
}
console.log(reverseArray(array1)); // ["if", "never", "sometimes", "always", "maybe", "no", "yes"]
console.log(reverseArray(array2)) // [1, 3, 6, 5, 9, 2, 8, 5]
答案 16 :(得分:0)
// array-reverse-polyfill.js v1
Array.prototype.reverse = Array.prototype.reverse || function() {
return this.sort(function() {
return -1;
});
};
如果您使用的是现代浏览器,则可以使用本机反向功能。如果它不是现代的,这个 polyfill 会为你添加这个功能。所以就用它吧:
let a = [1,2,3,4,5];
a.reverse();
console.log(a);