【问题标题】:Is there a SortedList<T> class in .NET?.NET 中是否有 SortedList<T> 类?
【发布时间】:2010-09-28 05:40:54
【问题描述】:

我需要根据它们的内容对一些对象进行排序(实际上是根据它们的属性之一,这不是关键,并且可能在不同的对象之间重复)。

.NET 提供了两个类(SortedDictionarySortedList),它们都是使用二叉树实现的。它们之间的唯一区别是

  • SortedList 使用的内存少于 SortedDictionary。
  • SortedDictionary 对未排序的数据具有更快的插入和删除操作,O(log n) 相对于 SortedList 的 O(n)。
  • 如果列表是从排序数据一次性填充的,SortedList 比 SortedDictionary 快。

我可以使用List, 来实现我想要的,然后使用它的Sort() 方法和IComparer 的自定义实现,但这并不省时,因为我每次都需要对整个列表进行排序插入一个新对象,而一个好的 SortedList 只会在正确的位置插入项目。

我需要的是一个带有 RefreshPosition(int index) 的 SortedList 类,用于仅移动已更改(或插入)的对象,而不是在每次内部对象更改时重新使用整个列表。

我是否遗漏了一些明显的东西?

【问题讨论】:

  • .Net 的另一个主要 WTF。它有一些很棒的东西,但我很震惊他们没有这方面的课程,也没有你认为很常见的其他东西。
  • .NET 现在提供了一个SortedSet&lt;T&gt;,但是这个still 不支持重复。他们一定有某种意识形态问题,支持带有重复的排序列表。

标签: c# .net sorting collections


【解决方案1】:

也许我很慢,但不是最简单的实现吗?

class SortedList<T> : List<T>
{
    public new void Add(T item)
    {
        Insert(~BinarySearch(item), item);
    }
}

http://msdn.microsoft.com/en-us/library/w4e7fxsh.aspx


不幸的是,Add 是不可覆盖的,所以我不得不 new 它,当你有 List&lt;T&gt; list = new SortedList&lt;T&gt;; 时,这不是很好,我实际上需要这样做......所以我继续重建整个东西……

class SortedList<T> : IList<T>
{
    private List<T> list = new List<T>();

    public int IndexOf(T item)
    {
        var index = list.BinarySearch(item);
        return index < 0 ? -1 : index;
    }

    public void Insert(int index, T item)
    {
        throw new NotImplementedException("Cannot insert at index; must preserve order.");
    }

    public void RemoveAt(int index)
    {
        list.RemoveAt(index);
    }

    public T this[int index]
    {
        get
        {
            return list[index];
        }
        set
        {
            list.RemoveAt(index);
            this.Add(value);
        }
    }

    public void Add(T item)
    {
        list.Insert(~list.BinarySearch(item), item);
    }

    public void Clear()
    {
        list.Clear();
    }

    public bool Contains(T item)
    {
        return list.BinarySearch(item) >= 0;
    }

    public void CopyTo(T[] array, int arrayIndex)
    {
        list.CopyTo(array, arrayIndex);
    }

    public int Count
    {
        get { return list.Count; }
    }

    public bool IsReadOnly
    {
        get { return false; }
    }

    public bool Remove(T item)
    {
        var index = list.BinarySearch(item);
        if (index < 0) return false;
        list.RemoveAt(index);
        return true;
    }

    public IEnumerator<T> GetEnumerator()
    {
        return list.GetEnumerator();
    }

    IEnumerator IEnumerable.GetEnumerator()
    {
        return list.GetEnumerator();
    }
}

或者也许这样的东西是更合适的Remove函数...

    public bool Remove(T item)
    {
        var index = list.BinarySearch(item);
        if (index < 0) return false;
        while (((IComparable)item).CompareTo((IComparable)list[index]) == 0)
        {
            if (item == list[index])
            {
                list.RemoveAt(index);
                return true;
            }
            index++;
        }
        return false;
    }

假设项目可以比较相等但不相等......

【讨论】:

  • 感谢您的课程。一个编辑: Add 方法不应该更像以下,以避免 ArgumentOutOfRangeException 异常吗? ` public virtual void Add(T item) { int index = list_.BinarySearch(item); list_.Insert(index >= 0 ? index : ~index, item); } `
  • 可能。我想我没有用重复的项目进行测试:-)
  • @Mark 你不需要写Remove 方法。它基本上是 O(n)。您可以改为将比较器传递给 list.BinarySearch 方法。它会给你正确的索引来删除。在您的情况下:index = list.BinarySearch(item, Comparer&lt;T&gt;.Default),然后是 list.RemoveAt(index)。它是 O(n - 索引)。略有收获:) 而且你 Suncat 是对的。我将提供an answer here 带来这两个变化。
  • @Mark 但实际上删除项目是 O(n),在你找到它之后,因为它是一个列表,所以虽然你可以在不到 O(n) 的时间内找到要删除的项目,@ 987654334@ 仍然是 O(n)。
  • @Mark 这就是排序数据结构通常是基于树的数据结构,而不是基于数组/基于数组的数据结构的原因,这使得SortedList(或这些自制的变体)几乎永远不会成为任何合适的数据结构给定的任务。如果您需要在不断变异的同时对数据进行排序,或者您想要一个常规的List,那么您通常最好使用基于树的结构,当您完成变异时不时对其进行排序(如所讨论的)在乔恩的回答中)。
【解决方案2】:

我最终决定写它:

class RealSortedList<T> : List<T>
    {
        public IComparer<T> comparer;

        public int SortItem(int index)
        {
            T item = this[index];
            this.RemoveAt(index);
            int goodposition=FindLocation(this[index], 0, this.Count);
            this.Insert(goodposition, item);
            return goodposition;
        }

        public int FindLocation(T item, int begin, int end)
        {
            if (begin==end)
                return begin;
            int middle = begin + end / 2;
            int comparisonvalue = comparer.Compare(item, this[middle]);
            if (comparisonvalue < 0)
                return FindLocation(item,begin, middle);
            else if (comparisonvalue > 0)
                return FindLocation(item,middle, end);
            else
                return middle;
        }
    }

【讨论】:

  • 继续,让它成为 IList 上的扩展,你知道你想这样做! :)
【解决方案3】:

不要忘记,将项目插入由数组支持的列表可能是一项昂贵的操作 - 插入一堆项目然后排序可能会更快,除非您真的需要在每次操作后进行排序。

或者,您总是可以包装一个列表并让您的添加操作找到正确的位置并将其插入那里。

【讨论】:

  • 是的,你绝对是对的,在大量插入的情况下,插入后只排序一次更快!不过,我认为 UpdatePosition(int index, IComparer comparer) 假设列表使用比较器进行排序并相应地更新索引将非常有用!
【解决方案4】:

我过去通过编写一个扩展方法来解决这个问题,该方法在 IList 上执行二进制搜索,另一个执行插入操作。您可以在 CLR 源代码中查找正确的实现,因为有一个仅适用于数组的内置版本,然后只需将其调整为 IList 上的扩展。

其中一个“应该已经在 BCL 中”的东西。

【讨论】:

    【解决方案5】:

    我需要的是一个 SortedList 类 一个 RefreshPosition(int index) 移动 仅更改(或插入)的对象 而不是诉诸整个列表 每次内部对象发生变化时。

    当此类更新使索引无效时,为什么要使用 索引 进行更新?真的,我认为通过对象引用进行更新会更方便。您可以使用 SortedList 执行此操作 - 只需记住您的 Key 类型与从对象中提取可比较数据的函数的返回类型相同。

    class UpdateableSortedList<K,V> {
        private SortedList<K,V> list = new SortedList<K,V>();
        public delegate K ExtractKeyFunc(V v);
        private ExtractKeyFunc func;
    
        public UpdateableSortedList(ExtractKeyFunc f) { func = f; }
    
        public void Add(V v) {
            list[func(v)] = v;
        }
        public void Update(V v) {
            int i = list.IndexOfValue(v);
            if (i >= 0) {
                list.RemoveAt(i);
            }
            list[func(v)] = v;
        }
        public IEnumerable<T> Values { get { return list.Values; } }
    }
    

    我猜是这样的。

    【讨论】:

    • 正如我在帖子中所说,不同的对象 V 可以具有相同的“可比值”K。我认为 SortedList 不支持重复键;还是我弄错了?
    • 呃。我不应该在 SO 编辑器上花费这么多时间。您知道解决方案:自己实现一棵二叉树(以满足您的要求),或重用其中一个框架类。如果框架类不起作用,则将 SortedList 转换为 MultiSortedList。
    • 是的;这就是要走的路。太糟糕了,这个功能没有内置到 .net 框架中!
    猜你喜欢
    • 2011-04-09
    • 2017-06-13
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2010-11-09
    • 1970-01-01
    • 2010-10-10
    • 2018-03-09
    相关资源
    最近更新 更多