71

Is the sorting algorithm used by .NET's Array.Sort() method a stable algorithm?

GEOCHET
  • 21,119
  • 15
  • 74
  • 98
Pop Catalin
  • 61,751
  • 23
  • 87
  • 115
  • When does the difference between stable sort and unstable sort matter? I'm curious. – tuinstoel Jun 23 '09 at 17:59
  • 14
    @tuinstoel - When you want to preserver the initial ordering of elements. Let's say you have a list of products that is already sorted by some criteria (IE Alphabetically), if you sort by Category using a stable sort the original sorting criteria is preserved inside the groups of products that are of the same category, the products will now be sorted by category and alphabetically. – Pop Catalin Oct 30 '09 at 11:25
  • 1
    This is glaring omission. [C++](http://www.cplusplus.com/reference/algorithm/stable_sort/), [Golang](https://golang.org/pkg/sort/#Stable) and [Python](https://docs.python.org/3/library/stdtypes.html#list.sort) all offer stable in-place sort functions. – Colonel Panic Nov 29 '15 at 21:02
  • 1
    Feature request https://github.com/dotnet/corefx/issues/4696 – Colonel Panic Nov 29 '15 at 21:02

5 Answers5

76

From MSDN:

This implementation performs an unstable sort; that is, if two elements are equal, their order might not be preserved. In contrast, a stable sort preserves the order of elements that are equal.

The sort uses introspective sort. (Quicksort in version 4.0 and earlier of the .NET framework).

If you need a stable sort, you can use Enumerable.OrderBy.

Rasmus Faber
  • 48,631
  • 24
  • 141
  • 189
  • 2
    Just note to make answer actual. The sorting method has changed between 4.5 and 4.0, from a quick sort to an [introspective sort](http://ru.wikipedia.org/wiki/Introsort). – Razoomnick Aug 19 '13 at 14:02
  • 1
    [Here](https://en.wikipedia.org/wiki/Introsort) is the same article (introspective sort) in English. – Xynariz Sep 23 '14 at 21:34
  • 3
    OrderBy is not always a suitable substitute, because it returns a copy rather than sorts in-place. – Colonel Panic Nov 29 '15 at 21:04
73

Adding to Rasmus Faber's answer

Sorting in LINQ, via Enumerable.OrderBy and Enumerable.ThenBy, is a stable sort implementation, which can be used as an alternative to Array.Sort. From Enumerable.OrderBy documentation over at MSDN:

This method performs a stable sort; that is, if the keys of two elements are equal, the order of the elements is preserved. In contrast, an unstable sort does not preserve the order of elements that have the same key.

Also, any unstable sort implementation, like that of Array.Sort, can be stabilized by using the position of the elements in the source sequence or array as an additional key to serve as a tie-breaker. Below is one such implementation, as a generic extension method on any single-dimensional array and which turns Array.Sort into a stable sort:

using System;
using System.Collections.Generic;

public static class ArrayExtensions {

    public static void StableSort<T>(this T[] values, Comparison<T> comparison) {
        var keys = new KeyValuePair<int, T>[values.Length];
        for (var i = 0; i < values.Length; i++)
            keys[i] = new KeyValuePair<int, T>(i, values[i]);
        Array.Sort(keys, values, new StabilizingComparer<T>(comparison));
    }

    private sealed class StabilizingComparer<T> : IComparer<KeyValuePair<int, T>> 
    {
        private readonly Comparison<T> _comparison;

        public StabilizingComparer(Comparison<T> comparison) {
            _comparison = comparison;
        }

        public int Compare(KeyValuePair<int, T> x, 
                           KeyValuePair<int, T> y) {
            var result = _comparison(x.Value, y.Value);
            return result != 0 ? result : x.Key.CompareTo(y.Key);
        }
    }
}

Below is a sample program using StableSort from above:

static class Program 
{
    static void Main() 
    {
        var unsorted = new[] {
            new Person { BirthYear = 1948, Name = "Cat Stevens" },
            new Person { BirthYear = 1955, Name = "Kevin Costner" },
            new Person { BirthYear = 1952, Name = "Vladimir Putin" },
            new Person { BirthYear = 1955, Name = "Bill Gates" },
            new Person { BirthYear = 1948, Name = "Kathy Bates" },
            new Person { BirthYear = 1956, Name = "David Copperfield" },
            new Person { BirthYear = 1948, Name = "Jean Reno" },
        };

        Array.ForEach(unsorted, Console.WriteLine);

        Console.WriteLine();
        var unstable = (Person[]) unsorted.Clone();
        Array.Sort(unstable, (x, y) => x.BirthYear.CompareTo(y.BirthYear));
        Array.ForEach(unstable, Console.WriteLine);

        Console.WriteLine();
        var stable = (Person[]) unsorted.Clone();
        stable.StableSort((x, y) => x.BirthYear.CompareTo(y.BirthYear));
        Array.ForEach(stable, Console.WriteLine);
    }
}

sealed class Person 
{
    public int BirthYear { get; set; }
    public string Name { get; set; }

    public override string ToString() {
        return string.Format(
            "{{ BirthYear = {0}, Name = {1} }}", 
            BirthYear, Name);
    }
}

Below is the output from the sample program above (running on a machine with Windows Vista SP1 and .NET Framework 3.5 SP1 installed):

{ BirthYear = 1948, Name = Cat Stevens }
{ BirthYear = 1955, Name = Kevin Costner }
{ BirthYear = 1952, Name = Vladimir Putin }
{ BirthYear = 1955, Name = Bill Gates }
{ BirthYear = 1948, Name = Kathy Bates }
{ BirthYear = 1956, Name = David Copperfield }
{ BirthYear = 1948, Name = Jean Reno }

{ BirthYear = 1948, Name = Jean Reno }
{ BirthYear = 1948, Name = Kathy Bates }
{ BirthYear = 1948, Name = Cat Stevens }
{ BirthYear = 1952, Name = Vladimir Putin }
{ BirthYear = 1955, Name = Bill Gates }
{ BirthYear = 1955, Name = Kevin Costner }
{ BirthYear = 1956, Name = David Copperfield }

{ BirthYear = 1948, Name = Cat Stevens }
{ BirthYear = 1948, Name = Kathy Bates }
{ BirthYear = 1948, Name = Jean Reno }
{ BirthYear = 1952, Name = Vladimir Putin }
{ BirthYear = 1955, Name = Kevin Costner }
{ BirthYear = 1955, Name = Bill Gates }
{ BirthYear = 1956, Name = David Copperfield }
Community
  • 1
  • 1
Atif Aziz
  • 36,108
  • 16
  • 64
  • 74
20

As other answers have stated, Array.Sort isn't stable. However, the LINQ OrderBy methods (and OrderByDescending etc) are stable, which can be very useful.

Jon Skeet
  • 1,421,763
  • 867
  • 9,128
  • 9,194
3

No, it isn't:

This method uses the QuickSort algorithm. This implementation performs an unstable sort

Konrad Rudolph
  • 530,221
  • 131
  • 937
  • 1,214
-3

UPDATE: This code not stabilizing Array.Sort (ensure that the elements are always sorted in the same order):

public static class ComparisonExtensions
{
    public static Comparison<T> WithGetHashCode<T>(this Comparison<T> current)
    {
        return (x, y) =>
        {
            var result = current(x, y);
            if (result == 0)
                return x.GetHashCode() - y.GetHashCode();
            return result;
        };
    }
}

Use:

Comparison<Person> comparison = (x, y) => x.BirthYear.CompareTo(y.BirthYear);
Array.Sort(unstable, comparison.WithGetHashCode());
halority
  • 1
  • 2
  • You clearly dont understand the meaning of a stable sort. It does not matter `Comparison` you use, `Array.Sort` is unstable. – leppie Feb 13 '13 at 08:49
  • I don't thinks so. It's stabilized by HashCode, as far as objects has same HashCode sort it is unstable (it dosen't mean the reference equal). So i wrote the alorithm is stablized by HashCode, means as far as objects has differ HashCode it is stable. – halority Feb 13 '13 at 08:53
  • And what happens when I swap 2 'duplicate' elements before sorting? Do you expect the hashcode to suddenly change? – leppie Feb 13 '13 at 08:55
  • If you duplicate object, that means has same HashCode. – halority Feb 13 '13 at 08:57
  • "It does not matter Comparison you use" I don't say that Array.Sort will be stable, I also say like @Atif Aziz "any unstable sort implementation, like that of Array.Sort, can be stabilized" using my method, that's all. – halority Feb 13 '13 at 09:02
  • 2
    @leppie: you right, my fault. This code ensure that the elements are always sorted in the same order, not stabilizing Array.Sort() – halority Feb 13 '13 at 14:37