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

时间:2011-03-11 18:38:15

标签: javascript arrays performance

最近有人问我在Javascript中反转数组的最有效方法是什么。目前,我建议使用for循环并摆弄数组,但后来意识到有一个原生的Array.reverse()方法。

出于好奇心的缘故,任何人都可以通过展示示例或指向正确的方向帮助我探索这个问题,以便我可以读到这个吗?关于如何衡量绩效的任何建议也都很棒。

17 个答案:

答案 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();方法的算法是最简单的例子,它们看起来最干净。我希望将来他们的表现会好转。


另一个提及是,现代浏览器正在提高其阵列pushsplice操作的性能。

在Firefox 10中,使用数组forpush的{​​{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;
}

Benchmark!

如果您使用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;
})

它的速度是两次接近的速度的两倍,但除此之外,速度非常慢。

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

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

您可以在http://jsperf.com/js-array-reverse-vs-while-loop/19

找到基准测试

答案 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)

&#13;
&#13;
var numbers = [1,4,9,13,16];

console.log(numbers.sort(() => 1));
&#13;
&#13;
&#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);