web-dev-qa-db-ja.com

順序を保持するHashSet

挿入順序を保持するHashSetが必要ですが、フレームワークにこれを実装したものはありますか?

41
Sam Saffron

標準の.NET HashSetは挿入順序を保持しません。簡単なテストでは、事故のために挿入順序が保持される場合がありますが、保証されておらず、常にそのように機能するとは限りません。間にいくつかの削除を行うだけで十分であることを証明するため。

詳細については、この質問を参照してください。 HashSetは挿入順序を保持しますか?

挿入順序を保証するHashSetを簡単に実装しました。 Dictionaryを使用してアイテムを検索し、LinkedListを使用して順序を保持します。 3つの挿入、削除、ルックアップはすべてO(1)で引き続き機能します。

public class OrderedSet<T> : ICollection<T>
{
    private readonly IDictionary<T, LinkedListNode<T>> m_Dictionary;
    private readonly LinkedList<T> m_LinkedList;

    public OrderedSet()
        : this(EqualityComparer<T>.Default)
    {
    }

    public OrderedSet(IEqualityComparer<T> comparer)
    {
        m_Dictionary = new Dictionary<T, LinkedListNode<T>>(comparer);
        m_LinkedList = new LinkedList<T>();
    }

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

    public virtual bool IsReadOnly
    {
        get { return m_Dictionary.IsReadOnly; }
    }

    void ICollection<T>.Add(T item)
    {
        Add(item);
    }

    public bool Add(T item)
    {
        if (m_Dictionary.ContainsKey(item)) return false;
        LinkedListNode<T> node = m_LinkedList.AddLast(item);
        m_Dictionary.Add(item, node);
        return true;
    }

    public void Clear()
    {
        m_LinkedList.Clear();
        m_Dictionary.Clear();
    }

    public bool Remove(T item)
    {
        LinkedListNode<T> node;
        bool found = m_Dictionary.TryGetValue(item, out node);
        if (!found) return false;
        m_Dictionary.Remove(item);
        m_LinkedList.Remove(node);
        return true;
    }

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

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

    public bool Contains(T item)
    {
        return m_Dictionary.ContainsKey(item);
    }

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

この機能は、TKeyとTItemに同じ型引数を指定して KeyedCollection<TKey,TItem> を使用して簡単に取得できます。

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

AddRemoveContainsの一定の複雑さと順序の保持が必要な場合、.NET Framework4.5にはそのようなコレクションはありません。

サードパーティのコードで問題がない場合は、私のリポジトリを見てください(permissive MIT license): https://github.com/OndrejPetrzilka/Rock.Collections ==

OrderedHashSet<T>コレクションがあります:

  • 従来のHashSet<T>ソースコード(.NET Coreから)に基づく
  • 挿入の順序を保持し手動での並べ替えを許可します
  • 機能逆列挙
  • HashSet<T>同じ操作の複雑さがあります
  • AddおよびRemoveの操作は、HashSet<T>と比較して20%遅くなります。
  • アイテムごとにさらに8バイトのメモリを消費します
5