Array.sort()方法在不同浏览器中的稳定性是什么?

时间:2010-06-11 21:10:38

标签: javascript arrays sorting cross-browser stable-sort

我知道ECMA脚本规范没有指定用于排序数组的算法,也没有指定排序是否应该稳定。

我发现this information for Firefox指定firefox使用稳定排序。

有谁知道IE 6/7/8,Chrome和Safari?

5 个答案:

答案 0 :(得分:58)

自ES2019起,sort必须保持稳定。在通过ES2018的ECMAScript第1版中,它被允许不稳定。

Simple test case(忽略标题,如果引擎的排序稳定,第二组数字应该是顺序的)。注意:此测试用例不适用于某些版本的Chrome(技术上是V8),它们根据数组的大小切换排序算法,对小型阵列使用稳定排序,对较大阵列使用不稳定排序。 (Details。)请参阅问题的结尾,了解修改后的版本,使数组足够大以触发行为。

IE的排序一直稳定,只要我曾经使用它(所以IE6)。再次检查IE8,似乎仍然如此。

虽然你链接到的Mozilla页面说Firefox的排序是稳定的,但我肯定地说在Firefox 2.0之前(包括)并不总是如此。

一些粗略的结果:

  • IE6 +:稳定
  • Firefox< 3:不稳定
  • Firefox> = 3:稳定
  • Chrome< 70:不稳定
  • Chrome> = 70:稳定
  • Opera< 10:不稳定
  • Opera> = 10:稳定
  • Safari 4:稳定
  • Edge:对于长数组(>512 elements
  • 不稳定

Windows上的所有测试。

另请参阅: Fast stable sorting algorithm implementation in javascript

此测试用例(从here修改)将通过确保数组有足够的条目来选择“更有效”的排序方法来演示V8中的问题(例如,Node v6,Chrome< v70);这是用非常古老的JavaScript引擎编写的,因此没有现代功能:

function Pair(_x, _y) {
    this.x = _x;
    this.y = _y;
}
function pairSort(a, b) {
    return a.x - b.x;
}
var y = 0;
var check = [];
while (check.length < 100) {
    check.push(new Pair(Math.floor(Math.random() * 3) + 1, ++y));
}
check.sort(pairSort);
var min = {};
var issues = 0;
for (var i = 0; i < check.length; ++i) {
    var entry = check[i];
    var found = min[entry.x];
    if (found) {
        if (found.y > entry.y) {
            console.log("Unstable at " + found.i + ": " + found.y + " > " + entry.y);
            ++issues;
        }
    } else {
        min[entry.x] = {x: entry.x, y: entry.y, i: i};
    }
}
if (!issues) {
    console.log("Sort appears to be stable");
}

答案 1 :(得分:14)

我想分享我在{/ 1}}的C / C ++中经常使用的技巧。

JS'sort()允许指定比较函数。创建相同长度的第二个数组,并用0增加的数字填充它。

qsort()

这是原始数组的索引。我们要对第二个数组进行排序。制作自定义比较功能。

function stableSorted(array, compareFunction) {
  compareFunction = compareFunction || defaultCompare;
  var indicies = new Array(array.length);
  for (var i = 0; i < indicies.length; i++)
    indicies[i] = i;

它将从第二个数组中获取两个元素:将它们用作原始数组的索引并比较元素。

  indicies.sort(function(a, b)) {

如果元素恰好相等,那么比较它们的索引以使订单稳定。

    var aValue = array[a], bValue = array[b];
    var order = compareFunction(a, b);
    if (order != 0)
      return order;

在sort()之后,第二个数组将包含可用于以稳定的排序顺序访问原始数组元素的索引。

   if (a < b)
     return -1;
   else
     return 1;
  });

一般来说,稳定的排序算法只会越来越成熟,并且与好的qsort相比仍然需要更多的额外内存。我猜这就是为什么很少有规格要求稳定排序的原因。

答案 2 :(得分:5)

从V8 v7.0和Chrome 70开始,我们的Array.prototype.sort实施现已稳定。

以前,V8对带有more than 10 elements的数组使用了不稳定的QuickSort。现在,V8使用稳定的TimSort算法。

唯一仍具有不稳定的Array#sort实现的主要引擎JavaScript引擎是Chakra(在Microsoft Edge中使用)。 Chakra对带有more than 512 elements的数组使用QuickSort。对于较小的数组,它使用稳定的插入排序实现。

演示: https://mathiasbynens.be/demo/sort-stability

答案 3 :(得分:0)

如果有人觉得它有用,我有一个 polyfill,我现在正在删除它:

const stable = (count => {
    const
        array = new Array(count),
        buckets = {};

    let i, k, v;

    for (i = 0; i < count; ++i) {
        array[i] = [Math.floor(Math.random() * 3) + 1, i + 1];  // [1..3, 1..count]
    }

    array.sort((a, b) => a[0] - b[0]);

    for (i = 0; i < count; ++i) {
        [k, v] = array[i];

        if (buckets[k] > v) {
            return false;
        }

        buckets[k] = v;
    }

    return true;
// Edge's JS engine has a threshold of 512 before it goes unstable, so use a number beyond that:
})(600);

if (!stable) {
    const
        { prototype } = Array,
        { sort } = prototype;

    Object.defineProperty(prototype, 'sort', {
        configurable : true,

        value(sortFn) {
            const
                array = this,
                len = array.length,
                temp = new Array(len);

            let i;

            for (i = len; i-- > 0; /* empty */) {
                temp[i] = i;
            }

            sortFn = sortFn || defaultSort;

            sort.call(temp, (index1, index2) => sortFn(array[index1], array[index2]) || index1 - index2);

            // we cannot do this directly into array since we may overwrite an element before putting it into the
            // correct spot:
            for (i = len; i-- > 0; /* empty */) {
                temp[i] = array[temp[i]];
            }

            for (i = len; i-- > 0; /* empty */) {
                array[i] = temp[i];
            }

            return array;
        }
    });
}

答案 4 :(得分:-2)

如果您要查找应使用非原生排序算法的浏览器列表,我的建议是不要

而是在脚本加载并做出决定时进行排序健全性检查。

由于规范在这方面不需要特定的行为,即使在同一浏览器行中,它也不能免受以后的更改。

您可以向http://www.browserscope.org/提交补丁,以便在其套件中包含此类测试。但同样,特征检测优于浏览器检测。