SortedList <>, SortedDictionary <> và Dictionary <>


98

Tôi tìm thấy điều đó SortedList<TKey, TValue> SortedDictionary<TKey, TValue>Dictionary<TKey, TValue>triển khai các giao diện giống nhau.

  1. Khi nào chúng ta nên chọn SortedListvà bỏ SortedDictionaryqua Dictionary?
  2. Sự khác biệt giữa SortedListSortedDictionaryvề mặt ứng dụng là gì?

Câu trả lời:


101
  1. Khi lặp lại các phần tử trong một trong hai, các phần tử sẽ được sắp xếp. Không phải như vậy với Dictionary<T,V>.

  2. MSDN giải quyết sự khác biệt giữa SortedList<T,V>SortedDictionary<T,V>:

Lớp chung SortedDictionary (TKey, TValue) là cây tìm kiếm nhị phân với truy xuất O (log n), trong đó n là số phần tử trong từ điển. Về mặt này, nó tương tự như lớp chung SortedList (TKey, TValue). Hai lớp có mô hình đối tượng giống nhau và cả hai đều có truy xuất O (log n). Trường hợp hai lớp khác nhau là sử dụng bộ nhớ và tốc độ chèn và xóa:

SortedList (TKey, TValue) sử dụng ít bộ nhớ hơn SortedDictionary (TKey, TValue).

SortedDictionary (TKey, TValue) có các thao tác chèn và xóa nhanh hơn đối với dữ liệu chưa được sắp xếp: O (log n) trái ngược với O (n) cho SortedList (TKey, TValue).

Nếu danh sách được điền cùng một lúc từ dữ liệu đã sắp xếp, thì SortedList (TKey, TValue) sẽ nhanh hơn SortedDictionary (TKey, TValue).


21
Một sự khác biệt thực tế khác, đó là SortedListbạn có thể truy xuất bằng chỉ mục (trái ngược với truy xuất bằng khóa) và SortedDictionarybạn không thể.
Andrew Savinykh

65

nhập mô tả hình ảnh ở đây

Tôi muốn đề cập đến sự khác biệt giữa các từ điển.

Hình trên cho thấy Dictionary<K,V>tốc độ bằng hoặc nhanh hơn trong mọi trường hợp so với Sortedanalog, nhưng nếu thứ tự của các phần tử được yêu cầu, chẳng hạn như để in chúng, Sortedmột phần tử được chọn.

Src: http://people.cs.aau.dk/~normark/oop-csharp/html/notes/collections-note-time-complexity-dictionaries.html


1
Tổng quan tuyệt vời. Mặc dù không có trong câu hỏi ban đầu, nhưng cần lưu ý rằng nếu bạn chọn giữa các Immutablephiên bản của các từ điển này, các Sortedphiên bản này thường thực sự nhanh hơn khoảng 40-50% so với các phiên bản không được sắp xếp (vẫn còn O(log(n)), nhưng nhanh hơn đáng kể trên mỗi lần chọn) . Tuy nhiên, thời gian có thể khác nhau tùy thuộc vào cách sắp xếp đầu vào. Xem stackoverflow.com/a/30638592/111575
Abel

21

Để tóm tắt kết quả của Kiểm tra hiệu suất - SortedList so với SortedDictionary so với Từ điển và Hashtable , các kết quả từ tốt nhất đến kém nhất cho các tình huống khác nhau:

Sử dụng bộ nhớ:

SortedList<T,T>
Hashtable
SortedDictionary<T,T>
Dictionary<T,T>

Chèn:

Dictionary<T,T>
Hashtable
SortedDictionary<T,T>
SortedList<T,T>

Hoạt động tìm kiếm:

Hashtable
Dictionary<T,T>
SortedList<T,T>
SortedDictionary<T,T>

hoạt động vòng lặp foreach

SortedList<T,T>
Dictionary<T,T>
Hashtable
SortedDictionary<T,T>

1
Khi kiểm tra các kết quả thử nghiệm này, người ta có thể đặt câu hỏi về sự raison d'etre của SortedDictionary.
beawolf,

1
Nếu Collectionnhu cầu của bạn là như vậy sortedthì bạn có thể quên đi HashtableDictionary: nếu bạn điền Bộ sưu tập của mình trong một lần -> chọn SortedList, nhưng nếu bạn dự đoán bạn sẽ thường xuyên cần .Add.Removecác mục -> chọn SortedDictionary.
Ama

Có lẽ cần phải làm rõ sortednghĩa là gì : khi bạn thực hiện một For Each MyItem in Collectionthay vì được xử lý theo thứ tự mà bạn đã chỉnh sửa .Addcác mục ban đầu , thì a sorted Collectionsẽ xử lý chúng theo thứ tự dựa trên các tiêu chí trên các Keygiá trị (được định nghĩa trong một IComparer). Ví dụ: nếu Khóa của bạn là Chuỗi, thì Bộ sưu tập của bạn theo mặc định sẽ được xử lý theo thứ tự bảng chữ cái của Khóa, nhưng bạn luôn có thể xác định quy tắc sắp xếp tùy chỉnh.
Ama

9
  1. Khi bạn muốn bộ sưu tập được sắp xếp theo khóa khi bạn lặp lại nó. Nếu bạn không cần sắp xếp dữ liệu của mình, tốt hơn hết bạn chỉ nên chọn một Từ điển, nó sẽ có hiệu suất tốt hơn.

  2. SortedList và SortedDictionary khá giống nhau, nhưng được triển khai khác nhau, do đó có những điểm mạnh và điểm yếu khác nhau được giải thích ở đây .


8

Tôi có thể thấy các câu trả lời được đề xuất tập trung vào hiệu suất. Bài viết được cung cấp dưới đây không cung cấp bất kỳ điều gì mới về hiệu suất, nhưng nó giải thích các cơ chế cơ bản. Cũng lưu ý rằng nó không tập trung vào ba CollectionLoại được đề cập trong câu hỏi, nhưng giải quyết tất cả các Loại của System.Collections.Generickhông gian tên.

http://geekswithblogs.net/BlackRabbitCoder/archive/2011/06/16/c.net-fundamentals-choosing-the-right-collection-class.aspx

Trích xuất:

Từ điển <>

Từ điển có lẽ là lớp chứa kết hợp được sử dụng nhiều nhất. Từ điển là lớp nhanh nhất để tra cứu / chèn / xóa liên kết vì nó sử dụng bảng băm bên dưới . Vì các khóa được băm, nên loại khóa phải triển khai chính xác GetHashCode () và Equals () một cách thích hợp hoặc bạn nên cung cấp IEqualityComparer bên ngoài cho từ điển đang được xây dựng. Thời gian chèn / xóa / tra cứu các mục trong từ điển là thời gian không đổi được phân bổ - O (1) - có nghĩa là cho dù từ điển có lớn đến đâu thì thời gian cần thiết để tìm một thứ vẫn tương đối không đổi. Điều này rất mong muốn cho việc tra cứu tốc độ cao. Nhược điểm duy nhất là là từ điển, về bản chất sử dụng bảng băm, không có thứ tự, vì vậybạn không thể dễ dàng duyệt các mục trong Từ điển theo thứ tự .

SortedDictionary <>

SortedDictionary tương tự như Dictionary về cách sử dụng nhưng rất khác về cách thực hiện. Các SortedDictionary sử dụng một cây nhị phân dưới tấm chăn để duy trì các mục theo thứ tự kết thúc bằng phím . Như một hệ quả của việc sắp xếp, loại được sử dụng cho khóa phải triển khai chính xác IComp so sánh được để các khóa có thể được sắp xếp chính xác. Từ điển được sắp xếp đánh đổi một ít thời gian tra cứu để có khả năng duy trì các mục theo thứ tự, do đó thời gian chèn / xóa / tra cứu trong từ điển được sắp xếp là logarit - O (log n). Nói chung, với thời gian logarit, bạn có thể tăng gấp đôi kích thước của bộ sưu tập và nó chỉ phải thực hiện thêm một phép so sánh để tìm ra mục. Sử dụng SortedDictionary khi bạn muốn tra cứu nhanh nhưng cũng muốn duy trì bộ sưu tập theo thứ tự theo khóa.

SortedList <>

SortedList là lớp vùng chứa kết hợp được sắp xếp khác trong các vùng chứa chung. Một lần nữa SortedList, giống như SortedDictionary, sử dụng khóa để sắp xếp các cặp khóa-giá trị . Tuy nhiên, không giống như SortedDictionary, các mục trong SortedList được lưu trữ dưới dạng mảng các mục được sắp xếp. Điều này có nghĩa là việc chèn và xóa là tuyến tính - O (n) - vì việc xóa hoặc thêm một mục có thể liên quan đến việc chuyển tất cả các mục lên hoặc xuống trong danh sách. Tuy nhiên, thời gian tra cứu là O (log n) vì SortedList có thể sử dụng tìm kiếm nhị phân để tìm bất kỳ mục nào trong danh sách bằng khóa của nó. Vậy tại sao bạn lại muốn làm điều này? Chà, câu trả lời là nếu bạn tải lên trước SortedList, việc chèn sẽ chậm hơn, nhưng vì lập chỉ mục mảng nhanh hơn so với các liên kết đối tượng theo sau, nên việc tra cứu nhanh hơn một chút so với SortedDictionary. Một lần nữa, tôi sẽ sử dụng điều này trong các tình huống mà bạn muốn tra cứu nhanh và muốn duy trì bộ sưu tập theo thứ tự theo khóa và ở những nơi hiếm khi chèn và xóa.


Tóm tắt dự kiến ​​về các thủ tục cơ bản

Phản hồi rất được hoan nghênh vì tôi chắc chắn rằng tôi đã không làm đúng mọi thứ.

  • Tất cả các mảng đều có kích thước n.
  • Mảng không được sắp xếp = .Add / .Remove là O (1), nhưng .Item (i) là O (n).
  • Mảng đã sắp xếp = .Add / .Remove là O (n), nhưng .Item (i) là O (log n).

Từ điển

Ký ức

KeyArray(n) -> non-sorted array<pointer>
ItemArray(n) -> non-sorted array<pointer>
HashArray(n) -> sorted array<hashvalue>

Thêm vào

  1. Thêm HashArray(n) = Key.GetHash# O (1)
  2. Thêm KeyArray(n) = PointerToKey# O (1)
  3. Thêm ItemArray(n) = PointerToItem# O (1)

Tẩy

  1. For i = 0 to n, tìm inơi HashArray(i) = Key.GetHash # O (log n) (mảng đã sắp xếp)
  2. Xóa HashArray(i)# O (n) (mảng đã sắp xếp)
  3. Xóa KeyArray(i)# O (1)
  4. Xóa ItemArray(i)# O (1)

Nhận hàng

  1. For i = 0 to n, tìm inơi HashArray(i) = Key.GetHash# O (log n) (mảng đã sắp xếp)
  2. Trở về ItemArray(i)

Vòng qua

  1. For i = 0 to n, trở về ItemArray(i)

SortedDictionary

Ký ức

KeyArray(n) = non-sorted array<pointer>
ItemArray(n) = non-sorted array<pointer>
OrderArray(n) = sorted array<pointer>

Thêm vào

  1. Thêm KeyArray(n) = PointerToKey# O (1)
  2. Thêm ItemArray(n) = PointerToItem# O (1)
  3. For i = 0 to n, tìm inơi KeyArray(i-1) < Key < KeyArray(i)(sử dụng ICompare) # O (n)
  4. Thêm OrderArray(i) = n# O (n) (mảng đã sắp xếp)

Tẩy

  1. For i = 0 to n, tìm inơi KeyArray(i).GetHash = Key.GetHash# O (n)
  2. Xóa KeyArray(SortArray(i))# O (n)
  3. Xóa ItemArray(SortArray(i))# O (n)
  4. Xóa OrderArray(i)# O (n) (mảng đã sắp xếp)

Nhận hàng

  1. For i = 0 to n, tìm inơi KeyArray(i).GetHash = Key.GetHash# O (n)
  2. Trở về ItemArray(i)

Vòng qua

  1. For i = 0 to n, trở về ItemArray(OrderArray(i))

SortedList

Ký ức

KeyArray(n) = sorted array<pointer>
ItemArray(n) = sorted array<pointer>

Thêm vào

  1. For i = 0 to n, tìm iở đâu KeyArray(i-1) < Key < KeyArray(i)(sử dụng ICompare) # O (log n)
  2. Thêm KeyArray(i) = PointerToKey# O (n)
  3. Thêm ItemArray(i) = PointerToItem# O (n)

Tẩy

  1. For i = 0 to n, tìm inơi KeyArray(i).GetHash = Key.GetHash# O (log n)
  2. Xóa KeyArray(i)# O (n)
  3. Xóa ItemArray(i)# O (n)

Nhận hàng

  1. For i = 0 to n, tìm inơi KeyArray(i).GetHash = Key.GetHash# O (log n)
  2. Trở về ItemArray(i)

Vòng qua

  1. For i = 0 to n, trở về ItemArray(i)

0

Cố gắng chỉ định điểm hiệu suất cho từng trường hợp do @Lev trình bày, tôi đã sử dụng các giá trị sau:

  • O (1) = 3
  • O (log n) = 2
  • O (n) = 1
  • O (1) hoặc O (n) = 2
  • O (log n) hoặc O (n) = 1,5

Kết quả là (cao hơn = tốt hơn):

Dictionary:       12.0 
SortedDictionary:  9.0 
SortedList:        6.5

Tất nhiên, mỗi trường hợp sử dụng sẽ có nhiều trọng lượng hơn cho các hoạt động nhất định.


1
Theo quy tắc chung, trọng lượng của O (log n) sẽ là log (n) / log (2) (+1 mỗi khi n nhân đôi) trong khi trọng lượng của O (n) sẽ là n. Vì vậy, trọng số của bạn sẽ chính xác cho kích thước lên đến 4. Bất cứ điều gì vượt quá sẽ thấy tỷ lệ 2: 1 của bạn nhanh chóng tăng lên. Ví dụ nếu n = 100 thì bạn sẽ có O (log n) = 15. Theo một suy nghĩ tương tự, O (1) của bạn sẽ nặng 100. Kết luận: O (n) thua trận khá nhanh. Nếu không, điều đó có nghĩa là mảng của bạn nhỏ và hiệu quả không phải là điều đáng lo ngại.
Ama
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.