2010-09-08 18 views
21

Tại sao chỉ có một SortedList<TKey, TValue> trông giống như một từ điển, nhưng không có SortedList<T> đó thực sự chỉ là một danh sách luôn được sắp xếp?Tại sao không có SortedList <T> trong .NET?

Theo the MSDN documentation on SortedList, nó thực sự được thực hiện nội bộ dưới dạng mảng động là KeyValuePair<TKey, TValue> luôn được sắp xếp theo khóa. Lớp học đó có hữu ích hơn không khi bạn có danh sách bất kỳ loại nào T? Điều đó cũng không phù hợp với cái tên tốt hơn?

+1

hmm, một ý tưởng thú vị. Nhưng nếu tôi đã có một SortedList làm thế nào nó sẽ thực hiện phân loại (trong đó 'chìa khóa')? Tôi có phải làm cho Foo triển khai IComparable không? – RPM1984

+2

Phải đồng ý với bạn - với tên bạn thực sự không mong đợi lớp này chứa các cặp khóa-giá trị. Nó không phải là một từ điển, tất nhiên, vì bạn có thể có cùng một chìa khóa hiện diện trong danh sách nhiều lần. –

+0

@ RPM1984 - vâng, bạn có thể làm cho Foo IComparable hoặc bạn có thể cung cấp một so sánh khi bạn xây dựng danh sách. –

Trả lời

9

Mặc dù không ai thực sự có thể cho bạn biết tại sao không có SortedList<T>, có thể thảo luận tại sao SortedList lấy chìa khóa và giá trị. Từ điển ánh xạ khóa tới giá trị. Cách điển hình để làm điều này là với cây nhị phân, bảng băm và danh sách (mảng), mặc dù bảng băm phổ biến nhất vì chúng là O (1) cho hầu hết các hoạt động.

Hoạt động chính mà nó không hỗ trợ trong O (1) là lấy khóa tiếp theo theo thứ tự. Nếu bạn muốn có thể làm điều đó, bạn thường sử dụng một cây nhị phân, cho bạn một từ điển được sắp xếp.

Nếu bạn quyết định triển khai bản đồ dưới dạng danh sách, bạn sẽ giữ các phần tử được sắp xếp theo khóa để tra cứu là O (lg n), cung cấp cho bạn một từ điển được sắp xếp khác - dưới dạng danh sách được sắp xếp. Tất nhiên tên SortedDictionary đã được sử dụng, nhưng SortedList thì không. Tôi có thể đã gọi nó là SortedListDictionary hoặc SortedDictionaryList, nhưng tôi đã không gọi tên nó.

1

Đây là danh sách có phân loại đang được thực hiện bởi khóa. Tôi chỉ suy đoán nhưng bằng cách cung cấp khả năng xác định khóa tách biệt với phần tử, phần tử của bạn không cần phải so sánh được - chỉ có nhu cầu chính. Tôi sẽ tưởng tượng rằng trong trường hợp chung này tiết kiệm một số tiền hợp lý của mã đang được phát triển để thực hiện IComparable kể từ khi khóa có thể là một loại đã được so sánh.

+3

Câu trả lời này không có ý nghĩa gì cả. Nếu bạn muốn sắp xếp theo thứ bạn cần so sánh, bất kể bạn có gọi cái gì đó là “chìa khóa” hay “giá trị” hay không. Nếu khóa là loại đã được so sánh, thì rõ ràng tôi cũng có thể sắp xếp danh sách chỉ các khóa và nếu không, thì rõ ràng 'SortedList ' cũng không cung cấp chức năng mong muốn. – Timwi

+0

Quan điểm của tôi là, với hầu hết các trường hợp, với 'SortedList ', bạn sẽ phải triển khai IComparable cho một lớp và IComparable sẽ đơn giản thực hiện lại (hoặc ủy quyền chức năng) so sánh kiểu giá trị. Trong trường hợp đó, làm cho khóa sắp xếp rõ ràng đơn giản hóa việc triển khai thực hiện cho người dùng của lớp với chi phí của một số chi phí lưu trữ - giả sử rằng khóa được rút ra từ một thuộc tính lớp. Bạn rõ ràng có thể giữ một danh sách các phím được sắp xếp và danh sách các giá trị được liên kết theo cùng một thứ tự. Tôi tin rằng đó là cách SortedList được thực hiện. Không hiệu quả lắm. – tvanfosson

+1

@tvanfosson: Không chính xác. Bản thân lớp không cần phải so sánh được.Trong thực tế, lý do chính để có một SortedList là các tình huống mà lập trình viên sẽ cung cấp một hàm tùy chỉnh làm so sánh, khi xây dựng danh sách. Ví dụ, nếu tôi muốn sắp xếp các điểm 2D trên y và sau đó x, tôi sẽ cung cấp một IComparer làm như vậy. Comparer như vậy không phải là vốn có của lớp điểm, cũng không phải là nó. Thay vào đó nó là một phần của việc sử dụng tùy chỉnh của tôi về những điểm đó – ToolmakerSteve

0

Nhận xét RPM là khá hợp lệ. Ngoài ra, với phần mở rộng LINQ, bạn có thể sắp xếp theo bất kỳ thuộc tính nào của T bằng cách sử dụng phương thức Mở rộng sắp xếp. Tôi nghĩ đó có thể là lý do chính đằng sau nó.

+3

Không có phương thức mở rộng 'Sắp xếp'. Nếu bạn có nghĩa là 'Danh sách .Sort' (mà không phải là một phương pháp mở rộng cũng không phải một phần của LINQ), sau đó sử dụng sau mỗi cuộc gọi đến' Add' và 'Insert' sẽ chậm hơn nhiều so với cần thiết. Nếu bạn có nghĩa là 'OrderBy', tức là * thậm chí * chậm hơn. – Timwi

+0

Khả năng sắp xếp không phải là câu trả lời cho câu hỏi. REASON: Điểm của một lớp 'Sorted..' là nó chỉ định kiểu khi bạn thêm/xóa các phần tử. Điều này hoàn toàn khác với việc gọi List.Sort bất cứ khi nào bạn cần nó được sắp xếp; nếu đó là giải pháp duy nhất, sau đó trong một số trường hợp sử dụng lập trình viên sẽ phải gọi sắp xếp mỗi khi họ thêm một yếu tố mới: cực kỳ chậm. Một lớp Sorted .. tốt sẽ hoạt động tốt hơn nhiều so với việc gọi Sort trên mỗi lần chèn (thông qua cấu trúc bên trong thích hợp). – ToolmakerSteve

2

Tôi nghĩ lý do có thể chỉ là List<T> đã có BinarySearchInsert, điều đó có nghĩa là việc triển khai danh sách luôn được sắp xếp của riêng bạn là không đáng kể.

Không phải điều này có nghĩa là lớp học SortedList<T> không thuộc về khung - chỉ có thể không phải là ưu tiên rất cao vì nó có thể dễ dàng được viết nhanh chóng bởi bất kỳ nhà phát triển nào cần.

Tôi nghĩ điều tương tự cũng đúng đối với HashSet<T>, vốn không tồn tại trước đây vì bạn có thể dễ dàng sử dụng Dictionary<T, byte> (ví dụ) để mô phỏng trước .NET 3.5.

Tôi biết đó là những gì tôi đã làm trong cả hai trường hợp: Tôi đã có một lớp UniqueSet<T> và một lớp AlwaysSortedList<T>, mà chỉ quấn một Dictionary<T, byte>List<T> (và sử dụng BinarySearchInsert), tương ứng.

+1

Nếu như một 'SortedList ' là ưu tiên quá thấp, thì chắc chắn 'SortedList ', cung cấp một tập con nghiêm ngặt của chức năng, thậm chí sẽ là ưu tiên thấp hơn. – Timwi

+0

@Timwi: Tôi không nghĩ đó là * khá * chính xác về 'SortedList '. Ý tôi là, về mặt triển khai, có vẻ như vậy. Nhưng lớp đó rõ ràng có nghĩa là được sử dụng như một từ điển, do đó tất cả các so sánh giữa 'SortedList ' và 'SortedDictionary ' trong tài liệu MSDN (và thực tế là 'SortedList 'thực hiện' IDictionary '). Tôi không biết * tại sao * họ sẽ ưu tiên điều này ở trên một danh sách được sắp xếp "thực"; thực tế vẫn là việc thực hiện một bản thân bạn thực sự không có nỗ lực. –

+1

Tôi nghĩ toàn bộ quan điểm của một khuôn khổ là vì vậy tôi không phải làm những điều tầm thường –

4

Hiện nay :)

public class SortedList<T> : ICollection<T> 
{ 
    private List<T> m_innerList; 
    private Comparer<T> m_comparer; 

    public SortedList() : this(Comparer<T>.Default) 
    { 
    } 

    public SortedList(Comparer<T> comparer) 
    { 
     m_innerList = new List<T>(); 
     m_comparer = comparer; 
    } 

    public void Add(T item) 
    { 
     int insertIndex = FindIndexForSortedInsert(m_innerList, m_comparer, item); 
     m_innerList.Insert(insertIndex, item); 
    } 

    public bool Contains(T item) 
    { 
     return IndexOf(item) != -1; 
    } 

    /// <summary> 
    /// Searches for the specified object and returns the zero-based index of the first occurrence within the entire SortedList<T> 
    /// </summary> 
    public int IndexOf(T item) 
    { 
     int insertIndex = FindIndexForSortedInsert(m_innerList, m_comparer, item); 
     if (insertIndex == m_innerList.Count) 
     { 
      return -1; 
     } 
     if (m_comparer.Compare(item, m_innerList[insertIndex]) == 0) 
     { 
      int index = insertIndex; 
      while (index > 0 && m_comparer.Compare(item, m_innerList[index - 1]) == 0) 
      { 
       index--; 
      } 
      return index; 
     } 
     return -1; 
    } 

    public bool Remove(T item) 
    { 
     int index = IndexOf(item); 
     if (index >= 0) 
     { 
      m_innerList.RemoveAt(index); 
      return true; 
     } 
     return false; 
    } 

    public void RemoveAt(int index) 
    { 
     m_innerList.RemoveAt(index); 
    } 

    public void CopyTo(T[] array) 
    { 
     m_innerList.CopyTo(array); 
    } 

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

    public void Clear() 
    { 
     m_innerList.Clear(); 
    } 

    public T this[int index] 
    { 
     get 
     { 
      return m_innerList[index]; 
     } 
    } 

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

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

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

    public bool IsReadOnly 
    { 
     get 
     { 
      return false; 
     } 
    } 

    public static int FindIndexForSortedInsert(List<T> list, Comparer<T> comparer, T item) 
    { 
     if (list.Count == 0) 
     { 
      return 0; 
     } 

     int lowerIndex = 0; 
     int upperIndex = list.Count - 1; 
     int comparisonResult; 
     while (lowerIndex < upperIndex) 
     { 
      int middleIndex = (lowerIndex + upperIndex)/2; 
      T middle = list[middleIndex]; 
      comparisonResult = comparer.Compare(middle, item); 
      if (comparisonResult == 0) 
      { 
       return middleIndex; 
      } 
      else if (comparisonResult > 0) // middle > item 
      { 
       upperIndex = middleIndex - 1; 
      } 
      else // middle < item 
      { 
       lowerIndex = middleIndex + 1; 
      } 
     } 

     // At this point any entry following 'middle' is greater than 'item', 
     // and any entry preceding 'middle' is lesser than 'item'. 
     // So we either put 'item' before or after 'middle'. 
     comparisonResult = comparer.Compare(list[lowerIndex], item); 
     if (comparisonResult < 0) // middle < item 
     { 
      return lowerIndex + 1; 
     } 
     else 
     { 
      return lowerIndex; 
     } 
    } 
} 
+0

Danh sách không thực hiện IList. Tuyệt vời :) –

+3

@MariuszJamro, thực sự 'SortedList ' không thể thực hiện 'IList ' bởi vì một số thao tác không có ý nghĩa trong ngữ cảnh của bộ sưu tập được sắp xếp - như chỉ mục thiết lập, 'InsertAt' và' RemoveAt' – ironstone13

+1

@Tal bạn có thể sử dụng BinarySearch thay vì FindIndexForSortedInsert – yacc

2

Tôi nghĩ rằng con đường để đi với vấn đề này là để thực hiện một phương pháp khuyến nông mà thêm vào List<T> một cách sắp xếp (chỉ cần 2 dòng mã;), và sau đó List<T> có thể được sử dụng làm danh sách được sắp xếp (giả sử bạn tránh sử dụng List.Add(...)):

public static void AddSorted<T>(this List<T> list, T value) 
    { 
     int x = list.BinarySearch(value); 
     list.Insert((x >= 0) ? x : ~x, value); 
    }