【问题标题】:What is the stability of the Array.sort() method in different browsers?Array.sort() 方法在不同浏览器中的稳定性如何?
【发布时间】:2010-06-11 21:10:37
【问题描述】:

我知道 ECMA Script 规范没有指定使用哪种算法对数组进行排序,也没有指定排序是否应该是稳定的。

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

有人知道 IE 6/7/8、Chrome 和 Safari 吗?

【问题讨论】:

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


    【解决方案1】:

    从 ES2019 开始,sort 需要稳定。在 ECMAScript 第 1 版到 ES2018 中,它被允许不稳定。

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

    只要我用过,IE 的排序就一直很稳定(所以 IE6)。在 IE8 中再次检查,情况似乎仍然如此。

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

    一些粗略的结果:

    • IE6+:稳定
    • Firefox
    • Firefox >= 3:稳定
    • Chrome
    • Chrome >= 70:稳定
    • Opera
    • Opera >= 10:稳定
    • Safari 4:稳定
    • Edge:长阵列不稳定 (>512 elements)

    Windows 上的所有测试。

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

    这个测试用例(修改自here)将通过确保数组有足够的条目来选择“更有效”的排序方法来证明 V8 中的问题(例如,Node v6、Chrome

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

    【讨论】:

    • 看起来 Chrome (V8) 将保持这种状态:code.google.com/p/v8/issues/detail?id=90
    • ofb.net/~sethml/is-sort-stable.html ? (我把它保存在网络档案中以防万一)
    • 从 IE9 开始,IE 排序不再稳定。 Chrome 对于
    • @JamesMontagne:是的,皮埃尔的链接确认
    • Chrome 自 v70 以来具有稳定的排序,Node.js 自 v11 以来具有稳定的排序。
    【解决方案2】:

    我想分享一个我经常在 C/C++ 中为qsort() 使用的技巧。

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

    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;
    

    如果元素恰好相等,则比较它们的索引以使顺序稳定。

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

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

      var sorted = new Array(array.length);
      for (var i = 0; i < sorted.length; i++)
        sorted[i] = array[indicies[i]];
      return sorted;
    }
    
    // The default comparison logic used by Array.sort(), if compareFunction is not provided:
    function defaultCompare(a, b) {
      a = String(a);
      b = String(b);
      if (a < b) return -1;
      else if (a > b) return 1;
      else return 0;
    }
    

    一般来说,稳定的排序算法才刚刚成熟,与好的 ol' qsort 相比,仍然需要更多的额外内存。我想这就是为什么很少有规范要求稳定排序的原因。

    【讨论】:

      【解决方案3】:

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

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

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

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

      【讨论】:

      • 您能否简要解释一下为什么使用 QuickSort 的旧 V8 实现被认为是不稳定的?不管怎样,祝贺你的出色工作。
      • QuickSort 通常不稳定,因为分区的工作方式。存在稳定的 QuickSort 版本,但它们需要额外的内存并且效率不高。
      • 它们都应该保证在未来保持稳定:github.com/tc39/ecma262/pull/1340 编辑:哈哈好吧 PR 实际上是你的。 ^^'
      【解决方案4】:

      如果有人觉得它有用,我有一个 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;
              }
          });
      }

      【讨论】:

        【解决方案5】:

        如果您正在寻找应该使用非本地排序算法的浏览器列表,我的建议是不要

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

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

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

        【讨论】:

        • 我不确定你是否可以编写一个能保证排序稳定的健全性检查。它可能前一刻看起来很稳定,但下一刻就变得不稳定。例如,如果排序以某种方式依赖于 JavaScript 对象在内存中的位置,就可能发生这种情况——这可能是不可预测的。
        • @RichDougherty -- 我相信你不能。您无法通过运行来证明排序算法是稳定的!这就像试图通过在街区周围开一圈来证明汽车是可靠的。你必须分析算法和实现。
        • @Malvolio,我想我们同意。我的意思是,如果测试现在通过,那么肯定不能保证将来通过,因此为稳定性进行加载时检查是徒劳的。
        • @RichDougherty -- 重新阅读您的 cmets,我现在意识到“不确定”是 litotes。
        • 虽然您在理论上是正确的,但实际上很容易生成一个数据集和排序函数,如果排序稳定,排序算法很可能是稳定的,尤其是对于较大的数据放。当然,很容易证明一个排序是不稳定的。这是我创建的示例:jsfiddle.net/1o5qgfzt 结果显示在控制台中。在 Chrome 中,长度为 10 的数组会稳定排序,超过 10 的数组则不稳定。
        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2012-09-21
        • 2017-08-01
        • 1970-01-01
        • 2010-09-13
        相关资源
        最近更新 更多