【问题标题】:Observable order set in .NET?.NET 中设置的可观察顺序?
【发布时间】:2015-09-15 14:39:44
【问题描述】:

我需要一个按顺序添加的集合,就像列表一样。

该集合也可能是可观察的。

.NET 4 中有这样的内置集合吗?

【问题讨论】:

  • @andrew.cuthbert 一个列表可以有重复,我需要一个集合
  • 一组不是有序的,非常基本的限制。如果要保持顺序,则必须在检查集合中是否已存在之后将其复制到列表中。移除当然是 O(n) 痛苦的。
  • @RayOldProf 在添加项目之前,您始终可以使用List#Contains 检查重复项。但我不认为您正在寻找的特定行为是内置的。
  • 维护插入顺序的集合会很昂贵,因为有效实现集合的常用方法是在内部对事物进行排序。
  • 如果 RDBMS 可以在 O(log N) 中做到这一点,那么内存中的结构也可以做到这一点。这不是低效的。

标签: c# .net set


【解决方案1】:

据我所知,.NET 中没有这种类型。我最近需要这个并最终自己实现了它;没那么难。

诀窍是将Dictionary<T, LinkedListNode<T>>LinkedList<T> 结合起来。使用字典在 O(1) 时间内查询键和值,并使用列表以插入顺序进行迭代。您需要字典而不是集合,因为您希望能够调用LinkedList<T>.Remove(LinkedListNode<T>) 而不是LinkedList<T>.Remove(T)。前者的时间复杂度为 O(1),后者的时间复杂度为 O(n)。

【讨论】:

  • 你实现开源了吗,可以分享一下吗?
【解决方案2】:

听起来您需要只读队列。在 .Net 中,我们内置了 Queue 类,但没有内置 ReadOnly Queue。 为了确保没有重复值,您可以使用包含检查

有一个 Nuget 包具有 ImmutableQueue。不确定它是否可以帮助你。 这会在每次 Enqueue 或 Dequeue 操作完成时创建新的 Queue 对象。 https://msdn.microsoft.com/en-us/library/dn467186(v=vs.111).aspx

【讨论】:

    【解决方案3】:

    我想您可以同时使用 SortedDictionary<>Dictionary<> 来执行此操作。

    假设您永远不会做超过int.MaxValue 插入到集合中,您可以使用整数“序列号”作为SortedDictionary 的键,以跟踪插入顺序中插入的项目。

    除此之外,您还需要使用 Dictionary 将项目映射到用于插入它们的序列号。

    把它放在一个类和一个演示程序中:(不是线程安全的!)

    using System;
    using System.Collections;
    using System.Collections.Generic;
    
    namespace Demo
    {
        public sealed class SequencedSet<T>: IEnumerable<T>
        {
            private readonly SortedDictionary<int, T> items = new SortedDictionary<int, T>();
            private readonly Dictionary<T, int> order = new Dictionary<T, int>();
    
            private int sequenceNumber = 0;
    
            public void Add(T item)
            {
                if (order.ContainsKey(item))
                    return; // Or throw if you want.
    
                order[item] = sequenceNumber;
                items[sequenceNumber] = item;
                ++sequenceNumber;
            }
    
            public void Remove(T item)
            {
                if (!order.ContainsKey(item))
                    return; // Or throw if you want.
    
                int sequence = order[item];
    
                items.Remove(sequence);
                order.Remove(item);
            }
    
            public bool Contains(T item)
            {
                return order.ContainsKey(item);
            }
    
            public IEnumerator<T> GetEnumerator()
            {
                return items.Values.GetEnumerator();
            }
    
            IEnumerator IEnumerable.GetEnumerator()
            {
                return GetEnumerator();
            }
        }
    
        internal class Program
        {
            private static void Main()
            {
                var test = new SequencedSet<string>();
    
                test.Add("One");
                test.Add("Two");
                test.Add("Three");
                test.Add("Four");
                test.Add("Five");
    
                test.Remove("Four");
                test.Remove("Two");
    
                foreach (var item in test)
                    Console.WriteLine(item);
            }
        }
    }
    

    这对于插入和删除应该是相当高效的,但它当然会占用双倍的内存。如果您要进行大量插入和删除,您可以使用long 而不是int 进行序列编号。

    不幸的是,如果您删除的次数超过 2^63 次,即使这样也行不通 - 尽管我认为这应该绰绰有余...

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2013-04-02
      • 2018-06-07
      • 2020-01-13
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多