【问题标题】:Set collection preserving insertion order设置集合保留插入顺序
【发布时间】:2011-01-23 23:23:11
【问题描述】:

我需要一个行为类似于 Set 并保留元素插入顺序的集合。

是否有一个,或者我必须自己实现它?

最好的实现是什么?

【问题讨论】:

  • 这样的收集如何运作?如果插入一个元素作为第一个元素,然后在 20 次迭代后再次插入,应该是元素 1 还是元素 20?
  • 对于我的任务,我需要它是元素 1。

标签: .net collections set


【解决方案1】:

它在 .NET 中不存在,但您可以使用 ListDistinct LINQ 扩展方法来模拟它,这应该保留底层 List 的顺序。

【讨论】:

  • 这非常令人沮丧,不是吗?同样的挫败感在 PowerShell 中使用 Hashtable 作为字典也丢失了插入顺序。
  • 我需要快速索引,但可以承受慢速插入,所以我将求助于List,并将编写扩展方法AddUnique 来实现Set语义。
  • LINQ 的Distinct 确实保留了当前实现的顺序,但MSDN documentation 特别声明它是无序的。谨慎行事。
【解决方案2】:

OrderedDictionary 会做你想做的事吗?

虽然它不是通用的(意味着它返回的所有内容都必须强制转换或取消装箱)并且是字典(不是集合),但它会按照您想要的方式运行。您可以轻松地使用诸如nulltrue 之类的任意值作为值,并让键成为您的集合的成员。

这是一个可能的实现:

public class OrderedSet : ICollection, IEnumerable
{
    OrderedDictionary dict = new OrderedDictionary();
    public void Add(object member)
    {
        dict.Add(member, null);
    }
    public void Clear()
    {
        dict.Clear();
    }
    public void CopyTo(Array array, int index)
    {
        for (int i = 0; i < Count; i++)
            array[i + index] = dict[i];
    }
    public bool Contains(object member)
    {
        return dict.Contains(member);
    }
    public void Insert(int index, object member)
    {
        dict.Insert(index, member, null);
    }
    public void Remove(object member)
    {
        dict.Remove(member);
    }
    public void RemoveAt(int index)
    {
        dict.RemoveAt(index);
    }
    IEnumerator IEnumerable.GetEnumerator()
    {
        return dict.Keys.GetEnumerator();
    }

    public int Count { get { return dict.Count; } }
    public ICollection Members { get { return dict.Keys; } }
    bool ICollection.IsSynchronized { get { return dict.IsSynchronized; } }
    object ICollection.SyncRoot { get { return dict.SyncRoot; } }
}

【讨论】:

  • 它会起作用,但他必须基本上只使用键而不是值。不理想,但它会完成工作。
【解决方案3】:

List inCountryList = new ArrayList(); . . . 设置 countrySet = new LinkedHashSet( inCountryList );

LinkedHashSet 不允许重复,请维护插入顺序。

【讨论】:

    【解决方案4】:

    创建一个很容易:

    public class InsertionOrderSet<T> : KeyedCollection<T,T>
    {
        protected override T GetKeyForItem(T item)
        {
            return item;
        }
    }
    

    警告:通过.Add(T) 插入重复项将导致ArgumentExceptions,这与HashSet&lt;T&gt; 不同,后者在这种情况下只会返回false

    【讨论】:

      【解决方案5】:

      我意识到这是一篇旧文章,但我最近需要类似的东西,并且认为如果有人想要一个通用序列来维护添加的订单项目(以及让您在之前和之后插入),这个实现可能会有所帮助任何给定的项目)。我确信有人有更有效的方法来完成这项工作,但这确实有效。

      public class Sequence<T> : ICollection<T>
      {
          private readonly SortedList<long, T> _baseList;
      
          public Sequence()
          {
              this._baseList = new SortedList<long, T>();
          }
      
          public IEnumerator<T> GetEnumerator()
          {
              return this._baseList.Values.GetEnumerator();
          }
      
          IEnumerator IEnumerable.GetEnumerator()
          {
              return this.GetEnumerator();
          }
      
          public void Add(T item)
          {
              this._baseList.Add(this._baseList.Count(), item);
          }
      
          public void AddAfter(T item, T newItem)
          {
              var currentIndex = this._baseList.IndexOfValue(item);
              if (currentIndex == _baseList.Count())
              {
                  this.Add(newItem);
              }
              else
              {
                  var itemsToMove = new SortedList<long, T>();
                  var total = Count;
                  for (var i = currentIndex + 1; i < total; i++)
                  {
                      itemsToMove.Add(i, _baseList[i]);
                      _baseList.Remove(i);
                  }
      
                  this.Add(newItem);
                  foreach (var itemToMove in itemsToMove)
                  {
                      this.Add(itemToMove.Value);
                  }
              }
          }
      
          public void AddBefore(T item, T newItem)
          {
              var currentIndex = this._baseList.IndexOfValue(item);
              var itemsToMove = new SortedList<long, T>();
              var total = Count;
              for (var i = currentIndex; i < total; i++)
              {
                  itemsToMove.Add(i, this._baseList[i]);
                  _baseList.Remove(i);
              }
      
              this.Add(newItem);
              foreach (var itemToMove in itemsToMove.Values)
              {
                  this.Add(itemToMove);
              }
          }
      
          public void Clear()
          {
              this._baseList.Clear();
          }
      
          public bool Contains(T item)
          {
              return this._baseList.ContainsValue(item);
          }
      
          public void CopyTo(T[] array, int arrayIndex)
          {
              this._baseList.Values.CopyTo(array, arrayIndex);
          }
      
          public bool Remove(T item)
          {
              try
              {
                  this._baseList.RemoveAt(this._baseList.IndexOfValue(item));
                  return true;
              }
              catch
              {
                  return false;
              }
          }
      
          public int Count
          {
              get
              {
                  return this._baseList.Count();
              }
          }
      
          public bool IsReadOnly
          {
              get
              {
                  return false;
              }
          }
      }
      

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 2012-03-24
        • 1970-01-01
        • 2010-10-14
        • 2012-12-12
        • 2014-12-17
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多