2009-10-12 30 views
30

Tôi cần một HashSet lưu giữ thứ tự chèn, có bất kỳ triển khai nào trong khung này không?HashSet giữ nguyên thứ tự

+2

Tôi tưởng tượng bạn có nghĩa là như [LinkedHashSet] của Java (http://docs.oracle.com/javase/7/docs/api/java/util/LinkedHashSet.html) – thejoshwolfe

+3

yeah the si mplest điều cần làm là để bọc một danh sách liên kết và hashset với nhau ... những gì tôi đã kết thúc làm trong quá khứ. hữu ích cho việc triển khai LRU –

+1

Theo định nghĩa của Set, không nên giữ bất kỳ thứ tự nào. –

Trả lời

21

Chuẩn .NET HashSet không giữ lại thứ tự chèn. Để kiểm tra đơn giản, thứ tự chèn có thể được bảo quản do tai nạn, nhưng nó không được bảo đảm và sẽ không luôn hoạt động theo cách đó. Để chứng minh rằng nó là đủ để làm một số loại bỏ ở giữa.

Xem câu hỏi này để biết thêm thông tin về rằng: Does HashSet preserve insertion order?

tôi có một thời gian ngắn thực hiện một HashSet mà đảm bảo trật tự chèn. Nó sử dụng Dictionary để tra cứu các mục và LinkedList để bảo vệ đơn đặt hàng. Tất cả ba công việc chèn, loại bỏ và tra cứu vẫn còn trong 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); 
    } 
} 
+8

OP không tuyên bố rằng 'HashSet ' giữ nguyên thứ tự ... trên thực tế, chúng đã tuyên bố nó không. Bạn chính xác không đồng ý với ai? – user7116

+0

Các câu trả lời khác được đánh giá cao cho biết rằng việc đặt hàng được đưa ra "ngoài hộp". Bây giờ chúng được đánh giá thấp. Tôi sẽ sửa đổi văn bản của tôi và rephrase nó trung lập. –

+1

Bạn nên thực sự cung cấp quá tải lấy một 'IEqualityComparer ' để chuyển sang cùng một quá tải của 'IDictionary '. Ra khỏi hộp, ví dụ kiểm tra bình đẳng của bạn thông qua các tham chiếu đối tượng (cho các lớp học, ít nhất) và cung cấp không có tùy chọn để thay đổi hành vi đó. Tuy nhiên, tôi hiểu rằng đây là một ví dụ đơn giản. –

15

Bạn có thể nhận được chức năng này dễ dàng bằng KeyedCollection<TKey,TItem> quy định cụ thể đối số kiểu tương tự cho TKey và TItem:

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

Khi bạn gọi 'Remove', nó sẽ gọi [' Remove (T) '] (http://msdn.microsoft.com/en-us/library/ms132413 (v = vs.110) .aspx) hoặc ['Remove (TKey)'] (http://msdn.microsoft.com/en-us/library/ms132459 (v = vs.110) .aspx)? Đầu tiên là O (n) và thứ hai là O (1). –

+2

Nó gọi là 'Remove (TKey)' bởi vì nó là một trong lớp có nguồn gốc cao nhất. Tuy nhiên, gọi Remove() khi bộ sưu tập được chọn làm Bộ sưu tập hoặc ICollection sẽ gọi quá trình tải xuống 'Xóa (T)'. – kcnygaard

+3

Làm rõ thêm: Cả hai 'Xóa (T)' và 'Xoá (TKey)' là O (n) vì 'KeyedCollection ' sử dụng một danh sách để lưu trữ các mục. – kcnygaard

3

Nếu bạn cần phức tạp liên tục của Add, Remove, Contains và bảo quản trật tự, sau đó không có bộ sưu tập như vậy trong .NET Framework 4.5.

Nếu bạn đang okay với mã bên thứ 3, hãy nhìn vào kho lưu trữ của tôi (giấy phép MIT dễ dãi): https://github.com/OndrejPetrzilka/Rock.Collections

OrderedHashSet<T> bộ sưu tập:

  • dựa trên mã nguồn cổ điển HashSet<T> (từ .NET Core)
  • bảo tồn thứ tự chèn vào và cho phép chỉnh sửa thủ công
  • tính năng đảo ngược liệt kê
  • phức tạp hoạt động tương tự như HashSet<T>
  • AddRemove hoạt động chậm hơn 20% so với HashSet<T>
  • tiêu thụ hơn 8 byte bộ nhớ cho mỗi mục
Các vấn đề liên quan