【问题标题】:How to force stable sort for ArrayList with a custom IComparer?如何使用自定义 IComparer 强制对 ArrayList 进行稳定排序?
【发布时间】:2020-07-31 17:11:30
【问题描述】:

根据the docs

要执行稳定的排序,您必须实现自定义 IComparer 界面。

但是根据this post.

文档似乎在说的是,您的唯一方法是 可以使用 ArrayList 获得稳定的排序。Sort 是使用 IComparer 以某种方式“知道”正在比较的项目的索引(一个 会想象通过使其运行初始传递来实现这一点 集合)并将该信息用作决胜局 否则相等的元素。

那么有谁知道如何正确实现一个 IComparer,它以某种方式“知道”正在比较的项目的索引?

【问题讨论】:

  • 只是出于好奇,为什么不使用通用的List 而不是ArrayList
  • "有谁知道如何正确实现 IComparer..." -- 是的。可能很多人都会。可能最简单的方法是创建一个新的元组集合,其中一个元素是要排序的对象,另一个是对象的原始索引。然后使用索引作为决胜局,正如现有 Stack Overflow 答案中所述。您尝试过任何东西吗?您具体在哪些方面需要帮助? Stack Overflow 不是“给我代码”网站。请努力自己编写比较器;如果需要,请发布一个带有良好 minimal reproducible example 和具体问题的新问题。
  • @vc74 我想用List,但是List只提供OrderBy(),OrderBy()创建一个新的排序List而不是直接排序,所以我尝试找一个直接排序的解决方案。跨度>
  • @PeterDuniho 我发现没有必要发布我在问题中尝试过的代码,因为我尝试过的代码类似于我已经链接的链接的代码(stackoverflow.com/q/5047566/4608491 & msdn docs)
  • @123iamking 使用 Linq 的OrderBy 并修改原始列表(list = list.OrderBy(...).ToList())很容易,这也是一个可行的选择。

标签: c# sorting arraylist stability


【解决方案1】:

你可以构建这样的东西(从this answer by Jon Skeet借用代码)

public sealed class IdentityEqualityComparer<T> : IEqualityComparer<T>
    where T : class
{
    public int GetHashCode(T value) => RuntimeHelpers.GetHashCode(value);

    public bool Equals(T left, T right) => left == right;
}

public class StableComparer : IComparer
{
    private readonly Dictionary<object, int> _itemsPosition = 
        new Dictionary<object, int>(new IdentityEqualityComparer<object>());

    private readonly IComparer _baseComparer;

    public StableComparer(IEnumerable collection, IComparer baseComparer)
    {
        _baseComparer = baseComparer;

        int index = 0;
        foreach (object item in collection)
        {
            _itemsPosition.Add(item, index);
            index++;
        }
    }

    public int Compare(object x, object y)
    {
        int baseResult = _baseComparer.Compare(x, y);
        if (baseResult != 0)
        {
            return baseResult;
        }

        int xIndex = _itemsPosition[x];
        int yIndex = _itemsPosition[y];

        return xIndex.CompareTo(yIndex);
    }
}

(我添加了相等比较器部分以确保按照@PeterDuniho 的建议使用引用相等)。

这段代码可以这样使用:

class Item
{
    public int Id { get; }
    public string Description { get; }

    public Item(int id, string description)
    {
        Id = id;
        Description = description;
    }
}

class ItemComparer : IComparer
{
    public int Compare(object x, object y) => ((Item)x).Id.CompareTo(((Item)y).Id);
}

class Program
{
    static void Main(string[] args)
    {
        var items = new ArrayList()
        {
            new Item(1, "Item 0 (1)"),
            new Item(3, "Item 1 (3)"),
            new Item(2, "Item 2 (2)"),
            new Item(3, "Item 3 (3)"),
            new Item(1, "Item 4 (1)"),
            new Item(4, "Item 5 (4)"),
            new Item(1, "Item 6 (1)")
        };

        Console.WriteLine("Not stable");
        SortAndDisplay((ArrayList)items.Clone(), new ItemComparer());

        Console.WriteLine("Stable");
        SortAndDisplay((ArrayList)items.Clone(), 
          new StableComparer(items, new ItemComparer()));
    }

    static void SortAndDisplay(ArrayList items, IComparer comparer)
    {
        items.Sort(comparer);

        foreach (var item in items)
        {
            Console.WriteLine(((Item)item).Description);
        }
    }
}

请注意,框架 4.5 中的 Sort 发生了变化,这可以改变“相等”项目的顺序。如果我在 Framework 4 中运行此代码,我会得到:

Not stable
Item 6 (1)
Item 4 (1)
Item 0 (1)
Item 2 (2)
Item 3 (3)
Item 1 (3)
Item 5 (4)
Stable
Item 0 (1)
Item 4 (1)
Item 6 (1)
Item 2 (2)
Item 1 (3)
Item 3 (3)
Item 5 (4)

我尝试将此代码发布到 .net Fiddle,但它不允许使用框架 4。 您也可以使用稳定的Linq's OrderBy

【讨论】:

  • 您应该尝试在任何实际包含重复元素的输入上测试您的代码。
  • @PeterDuniho 你介意提供更多关于哪里有错误的细节吗?
  • 抱歉,我应该更具体一些。因为您选择使用字典来维护您的地图,所以您的代码只有在被排序的项目本身没有实现IEquatable&lt;T&gt; 并且不覆盖GetHashCode()Equals() 时才能工作。许多类型,甚至那些需要自定义比较器的类型,都可以。 (此外,七个元素不足以证明/证明稳定与不稳定......这不足以从不稳定的排序中获得看似稳定的结果,尽管这不是这里的问题。)
  • @123iamking 是的,我在 4.5 上得到了相同的结果。这可能是由于算法从快速排序更改为内省排序。请注意,内省排序也不稳定,但似乎在这个特定的集合上给出了稳定的结果。
  • @123iamking 最后一点,Linq 的OrderBy 很稳定,可能是更好的选择。我已经有一段时间没有使用ArrayList了,上面代码中的投射量令人眼花缭乱;)
猜你喜欢
  • 2010-11-02
  • 2012-12-22
  • 1970-01-01
  • 2021-09-28
  • 1970-01-01
  • 2012-08-12
  • 2010-12-21
  • 1970-01-01
相关资源
最近更新 更多