Мне нужен HashSet, который сохраняет порядок вставки, существуют ли какие-либо реализации этого в рамках?
HashSet, который сохраняет порядок
Ответ 1
Стандартный .NET HashSet
не сохраняет порядок вставки.
Для простых тестов порядок вставки может быть сохранен из-за аварии, но он не гарантируется и не всегда будет работать таким образом. Чтобы доказать, что достаточно сделать некоторые удаления между ними.
См. этот вопрос для получения дополнительной информации: Сохраняет ли HashSet порядок вставки?
Я кратко выполнил HashSet
, который гарантирует порядок вставки. Он использует Dictionary
для поиска элементов и LinkedList
для сохранения порядка. Все три операции вставки, удаления и поиска все еще находятся в 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);
}
}
Ответ 2
Вы можете легко получить эту функциональность, используя KeyedCollection<TKey,TItem>
, указав один и тот же аргумент типа для TKey и TItem:
public class OrderedHashSet<T> : KeyedCollection<T, T>
{
protected override T GetKeyForItem(T item)
{
return item;
}
}
Ответ 3
Если вам нужна постоянная сложность Add
, Remove
, Contains
и сохранения порядка, то нет такой коллекции в .NET Framework 4.5.
Если вы в порядке со сторонним кодом, взгляните на мой репозиторий (разрешающая лицензия MIT): https://github.com/OndrejPetrzilka/Rock.Collections
Там OrderedHashSet<T>
коллекция:
- на основе классического
HashSet<T>
исходного кода (из .NET Core) - сохраняет порядок вставки и позволяет ручное переупорядочение
- функции обратное перечисление
- имеет те же операционные сложности, что и
HashSet<T>
Операции -
Add
иRemove
на 20% медленнее по сравнению сHashSet<T>
- потребляет еще 8 байт памяти на элемент