Cấu trúc dữ liệu .NET: ArrayList, List, HashTable, Dictionary, SortedList, SortedDipedia - Tốc độ, bộ nhớ và khi nào nên sử dụng từng loại?


213

.NET có rất nhiều cấu trúc dữ liệu phức tạp. Thật không may, một số trong số chúng khá giống nhau và tôi không chắc chắn khi nào nên sử dụng cái này và khi nào nên sử dụng cái khác. Hầu hết các sách C # và Visual Basic của tôi đều nói về chúng ở một mức độ nhất định, nhưng chúng không bao giờ thực sự đi sâu vào bất kỳ chi tiết thực sự nào.

Sự khác biệt giữa Array, ArrayList, List, Hashtable, Dictionary, SortedList và SortedDipedia là gì?

Những cái nào là vô số (IList - có thể thực hiện các vòng lặp 'foreach')? Những cái nào sử dụng cặp khóa / giá trị (IDict)?

Dấu chân bộ nhớ thì sao? Tốc độ chèn? Tốc độ truy hồi?

Có bất kỳ cấu trúc dữ liệu khác đáng nói?

Tôi vẫn đang tìm kiếm thêm chi tiết về việc sử dụng bộ nhớ và tốc độ (ký hiệu Big-O).


12
Bạn nên phá vỡ câu hỏi này. Bạn đang hỏi hai mươi điều khác nhau, một nửa trong số đó là một tìm kiếm google đơn giản có thể trả lời. Xin hãy cụ thể hơn; thật khó để giúp đỡ khi câu hỏi của bạn rất phân tán.

33
Tôi nghĩ về việc phá vỡ nó, nhưng nhận ra rằng ai đó có khả năng sẽ hợp nhất tất cả những câu trả lời này vào một nơi. Trong thực tế, nếu ai đó có thể đưa ra một bảng tóm tắt mọi thứ, nó có thể trở thành một tài nguyên tuyệt vời trên trang web này.
Pretzel

9
Câu hỏi này có thể được chuyển thành wiki không?
BozoJoe

1
Bài viết MSDN này bao gồm nhiều câu hỏi, bao gồm cây, biểu đồ và bộ, Kiểm tra mở rộng về cấu trúc dữ liệu
Ryan Fisher

1
Ryan, các bài viết tại liên kết đó là 14 tuổi, (12 tại thời điểm đăng bài). Lưu ý bên tôi đã đọc chúng cho tuần trước bản thân mình. nhưng chúng cũng không bao gồm công nghệ mới hơn và rất cần cập nhật. Và nhiều số liệu hiệu suất và ví dụ.
htm11h

Câu trả lời:


156

Off đỉnh đầu của tôi:

  • Array* - đại diện cho một mảng bộ nhớ trường học cũ - giống như một bí danh cho một type[]mảng bình thường . Có thể liệt kê. Không thể tự động phát triển. Tôi sẽ giả định tốc độ chèn và tốc độ rất nhanh.

  • ArrayList- mảng tự động phát triển. Thêm nhiều chi phí hơn. Có thể enum., Có thể chậm hơn một mảng bình thường nhưng vẫn khá nhanh. Chúng được sử dụng rất nhiều trong .NET

  • List- một trong những fav của tôi - có thể được sử dụng với generic, vì vậy bạn có thể có một mảng được gõ mạnh, ví dụ List<string>. Ngoài ra, hành động rất giốngArrayList

  • Hashtable- hashtable cũ đơn giản. Trường hợp xấu nhất từ ​​O (1) đến O (n). Có thể liệt kê các thuộc tính giá trị và khóa và thực hiện các cặp khóa / val

  • Dictionary - giống như trên chỉ được gõ mạnh thông qua thuốc generic, chẳng hạn như Dictionary<string, string>

  • SortedList- một danh sách chung được sắp xếp. Chậm lại khi chèn vì nó phải tìm ra nơi để đặt mọi thứ. Có thể enum., Có thể giống nhau khi truy xuất vì nó không phải dùng đến, nhưng việc xóa sẽ chậm hơn một danh sách cũ đơn giản.

Tôi có xu hướng sử dụng ListDictionarymọi lúc - một khi bạn bắt đầu sử dụng chúng được đánh máy mạnh mẽ với thuốc generic, thực sự rất khó để quay lại những cái không chung chung tiêu chuẩn.

Có rất nhiều cấu trúc dữ liệu khác - có những KeyValuePairthứ bạn có thể sử dụng để làm một số điều thú vị, cũng có những cấu trúc SortedDictionarycó thể hữu ích.


3
Bảng Hash là O (1), trường hợp xấu nhất (có va chạm) có thể là O (n)
Justin Bozonier

7
Có nhiều cấu trúc dữ liệu khác bạn cần thêm vào đây. như LinkedList, Skip List, Stack, Queue, Heap, Plants, Graphs. Đây là những cấu trúc dữ liệu rất quan trọng là tốt.
DarthVader

2
Đồng thời Từ điển được thêm vào .Net 4.0 cung cấp một từ điển chung với An toàn chủ đề
Harindaka

2
Đồng thời BlockingCollection <T> cung cấp một triển khai sản xuất / tiêu dùng an toàn cho chủ đề
Harindaka

7
ArrayListsử dụng các phương thức ảo, nhưng List<T>không. ArrayListđã được thay thế phần lớn bằng List<T>cho các bộ sưu tập tiêu chuẩn và Collection<T>như là một lớp cơ sở cho các bộ sưu tập tùy chỉnh. Hashtableđã được thay thế phần lớn bởi Dictionary<TKey, TValue>. Tôi khuyên bạn nên tránh ArrayListHashtablecho mã mới.
Sam Harwell

29

Nếu có thể, sử dụng thuốc generic. Điêu nay bao gôm:

  • Danh sách thay vì ArrayList
  • Từ điển thay vì HashTable

24

Đầu tiên, tất cả các bộ sưu tập trong .NET thực hiện IEnumerable.

Thứ hai, rất nhiều bộ sưu tập là bản sao vì thuốc generic đã được thêm vào trong phiên bản 2.0 của khung.

Vì vậy, mặc dù các bộ sưu tập chung có khả năng thêm các tính năng, đối với hầu hết các phần:

  • Danh sách là một triển khai chung của ArrayList.
  • Từ điển là một triển khai chung của Hashtable

Mảng là một bộ sưu tập kích thước cố định mà bạn có thể thay đổi giá trị được lưu trữ tại một chỉ mục nhất định.

SortedDixi là một IDadata được sắp xếp dựa trên các phím. SortedList là một IDadata được sắp xếp dựa trên một IComparer cần thiết.

Vì vậy, các triển khai IDixi (những hỗ trợ KeyValuePairs) là: * Hashtable * Dictionary * SortedList * SortedDixi

Một bộ sưu tập khác đã được thêm vào .NET 3.5 là Hashset. Nó là một bộ sưu tập hỗ trợ các hoạt động thiết lập.

Ngoài ra, LinkedList là một triển khai danh sách liên kết chuẩn (Danh sách là một danh sách mảng để truy xuất nhanh hơn).


20

Dưới đây là một vài lời khuyên chung cho bạn:

  • Bạn có thể sử dụng foreachtrên các loại thực hiện IEnumerable. IListvề cơ bản là một thuộc tính IEnumberableCountItem(truy cập các mục bằng cách sử dụng chỉ mục dựa trên zero). IDictionarymặt khác có nghĩa là bạn có thể truy cập các mục theo bất kỳ chỉ mục có thể băm nào.

  • Array, ArrayListListtất cả thực hiện IList. Dictionary, SortedDictionaryHashtablethực hiện IDictionary.

  • Nếu bạn đang sử dụng .NET 2.0 trở lên, bạn nên sử dụng các bản sao chung của các loại được đề cập.

  • Đối với sự phức tạp về thời gian và không gian của các hoạt động khác nhau trên các loại này, bạn nên tham khảo tài liệu của họ.

  • Cấu trúc dữ liệu .NET nằm trong System.Collectionskhông gian tên. Có các thư viện kiểu như PowerCollections cung cấp các cấu trúc dữ liệu bổ sung.

  • Để có được sự hiểu biết thấu đáo về cấu trúc dữ liệu, hãy tham khảo các tài nguyên như CLRS .


1
từ msdn , có vẻ như sắp xếp danh sách triển khai IDictnary - không phải IList
Haim Bendanan

Đã sửa. cảm ơn vì nhận xét Có vẻ như SortedList giữ một danh sách các khóa / giá trị, vì vậy về cơ bản nó đại diện cho dữ liệu của từ điển. Đừng nhớ cách lớp học này hoạt động khi tôi lần đầu tiên viết câu trả lời ...
blackwing

9

Cấu trúc dữ liệu .NET:

Nói thêm về lý do tại sao ArrayList và List thực sự khác nhau

Mảng

Như một người dùng tuyên bố, Mảng là bộ sưu tập "trường học cũ" (vâng, mảng được coi là một bộ sưu tập mặc dù không phải là một phần của System.Collections). Nhưng, "trường học cũ" về mảng là gì so với các bộ sưu tập khác, tức là những bộ bạn đã liệt kê trong tiêu đề của bạn (ở đây, ArrayList và List (Of T))? Hãy bắt đầu với những điều cơ bản bằng cách nhìn vào Mảng.

Để bắt đầu, Mảng trong Microsoft .NET là "các cơ chế cho phép bạn coi một số mục [liên quan đến logic] như một bộ sưu tập duy nhất" (xem bài viết được liên kết). Điều đó nghĩa là gì? Mảng lưu trữ các thành viên riêng lẻ (các phần tử) một cách tuần tự, lần lượt từng phần trong bộ nhớ với một địa chỉ bắt đầu. Bằng cách sử dụng mảng, chúng ta có thể dễ dàng truy cập các phần tử được lưu trữ tuần tự bắt đầu tại địa chỉ đó.

Ngoài điều đó và trái với lập trình 101 khái niệm phổ biến, Mảng thực sự có thể khá phức tạp:

Mảng có thể là một chiều, đa chiều hoặc bị xáo trộn (mảng răng cưa rất đáng để đọc). Bản thân mảng không động: một khi được khởi tạo, một mảng kích thước n dự trữ đủ không gian để chứa n số lượng đối tượng. Số lượng phần tử trong mảng không thể tăng hoặc thu hẹp. Dim _array As Int32() = New Int32(100)dự trữ đủ không gian trên khối bộ nhớ cho mảng để chứa 100 đối tượng kiểu nguyên thủy Int32 (trong trường hợp này, mảng được khởi tạo để chứa 0s). Địa chỉ của khối này được trả về _array.

Theo bài báo, Đặc tả ngôn ngữ chung (CLS) yêu cầu tất cả các mảng phải dựa trên zero. Mảng trong .NET hỗ trợ các mảng không dựa trên; tuy nhiên, điều này là ít phổ biến hơn. Là kết quả của "tính phổ biến" của các mảng dựa trên zero, Microsoft đã dành rất nhiều thời gian để tối ưu hóa hiệu suất của chúng ; do đó, các mảng một chiều, không dựa trên (sz) là "đặc biệt" - và thực sự là cách triển khai tốt nhất của một mảng (trái ngược với đa chiều, v.v.) - bởi vì các sz có các hướng dẫn ngôn ngữ trung gian cụ thể để thao tác chúng.

Mảng luôn được truyền bằng tham chiếu (dưới dạng địa chỉ bộ nhớ) - một phần quan trọng của câu đố Array cần biết. Trong khi họ thực hiện kiểm tra giới hạn (sẽ đưa ra lỗi), kiểm tra giới hạn cũng có thể bị vô hiệu hóa trên mảng.

Một lần nữa, trở ngại lớn nhất đối với mảng là chúng không thể thay đổi kích thước. Họ có công suất "cố định". Giới thiệu ArrayList và List (Of T) cho lịch sử của chúng tôi:

ArrayList - danh sách không chung chung

Các ArrayList (cùng với List(Of T)- mặc dù có một số khác biệt quan trọng, ở đây, giải thích sau) - có lẽ là suy nghĩ tốt nhất là việc bổ sung bên cạnh bộ sưu tập (theo nghĩa rộng). ArrayList kế thừa từ giao diện IList (hậu duệ của 'ICollection'). Bản thân ArrayLists thì cồng kềnh hơn - đòi hỏi nhiều chi phí hơn hơn - so với Danh sách.

IListkhông cho phép triển khai để coi ArrayLists là danh sách có kích thước cố định (như Mảng); tuy nhiên, ngoài chức năng bổ sung được thêm bởi ArrayLists, không có lợi thế thực sự nào khi sử dụng ArrayLists có kích thước cố định như ArrayLists (trên Mảng) trong trường hợp này chậm hơn rõ rệt.

Từ cách đọc của tôi, ArrayLists không thể bị lởm chởm: "Sử dụng mảng đa chiều làm các phần tử ... không được hỗ trợ". Một lần nữa, một cái đinh khác trong quan tài của ArrayLists. ArrayLists cũng không được "gõ" - có nghĩa là, bên dưới mọi thứ, ArrayList chỉ đơn giản là một mảng đối tượng động : Object[]. Điều này đòi hỏi rất nhiều quyền anh (ngầm) và unboxing (rõ ràng) khi triển khai ArrayLists, một lần nữa thêm vào chi phí của họ.

Suy nghĩ không có căn cứ: Tôi nghĩ rằng tôi nhớ hoặc đã đọc hoặc đã nghe từ một trong những giáo sư của mình rằng ArrayLists là đứa con khái niệm khốn của nỗ lực chuyển từ Mảng sang Bộ sưu tập kiểu Danh sách, tức là trong khi đã được cải tiến rất nhiều cho Mảng, chúng không còn là lựa chọn tốt nhất vì sự phát triển hơn nữa đã được thực hiện đối với các bộ sưu tập

Danh sách (Of T): ArrayList đã trở thành (và hy vọng là gì)

Sự khác biệt về mức sử dụng bộ nhớ đủ đáng kể để Danh sách (Of Int32) tiêu thụ ít bộ nhớ hơn 56% so với ArrayList chứa cùng loại nguyên thủy (8 MB so với 19 MB trong trình diễn được liên kết của quý ông ở trên: một lần nữa, được liên kết tại đây ) - mặc dù đây là kết quả được tổng hợp bởi máy 64 bit. Sự khác biệt này thực sự thể hiện hai điều: thứ nhất (1), một "đối tượng" kiểu Int32 đóng hộp (ArrayList) lớn hơn nhiều so với kiểu nguyên thủy Int32 thuần túy (Danh sách); thứ hai (2), sự khác biệt là theo cấp số nhân do hoạt động bên trong của máy 64 bit.

Vậy, sự khác biệt và Danh sách (Of T) là gì? MSDN định nghĩa một List(Of T)as, "... một danh sách các đối tượng được gõ mạnh có thể được truy cập bởi chỉ mục." Điều quan trọng ở đây là bit "được gõ mạnh": Danh sách (Of T) 'nhận ra các loại và lưu trữ các đối tượng là loại của chúng. Vì vậy, một Int32được lưu trữ như một Int32và không phải là một Objectloại. Điều này giúp loại bỏ các vấn đề gây ra bởi quyền anh và unboxing.

MSDN chỉ định sự khác biệt này chỉ phát huy khi lưu trữ các kiểu nguyên thủy và không phải các kiểu tham chiếu. Quá, sự khác biệt thực sự xảy ra trên quy mô lớn: hơn 500 yếu tố. Điều thú vị hơn là tài liệu MSDN đọc, "Lợi thế của bạn là sử dụng triển khai cụ thể theo kiểu của lớp List (Of T) thay vì sử dụng lớp ArrayList ...."

Về cơ bản, List (Of T) là ArrayList, nhưng tốt hơn. Nó là "tương đương chung" của ArrayList. Giống như ArrayList, nó không được đảm bảo để được sắp xếp cho đến khi được sắp xếp (đi hình). Danh sách (Of T) cũng có một số chức năng được thêm vào.


5

Tôi đồng cảm với câu hỏi - Tôi cũng thấy (tìm thấy?) Sự lựa chọn hoang mang, vì vậy tôi đã đặt ra một cách khoa học để xem cấu trúc dữ liệu nào là nhanh nhất (tôi đã thử nghiệm bằng VB, nhưng tôi tưởng tượng C # sẽ giống nhau, vì cả hai ngôn ngữ làm điều tương tự ở cấp độ CLR). Bạn có thể thấy một số kết quả đo điểm chuẩn được thực hiện bởi tôi ở đây (cũng có một số cuộc thảo luận về loại dữ liệu nào là tốt nhất để sử dụng trong trường hợp nào).


3

Họ đánh vần khá tốt trong intellisense. Chỉ cần gõ System.Collections. hoặc System.Collections.Generics (ưa thích) và bạn sẽ nhận được một danh sách và mô tả ngắn về những gì có sẵn.


3

Hashtables / Từ điển là hiệu suất O (1), có nghĩa là hiệu suất không phải là một chức năng của kích thước. Đó là điều quan trọng cần biết.

EDIT: Trong thực tế, độ phức tạp thời gian trung bình cho Hashtable / Dictionary <> tra cứu là O (1).


5
Không có thứ gọi là "hiệu suất". Sự phức tạp phụ thuộc vào hoạt động. Ví dụ: nếu bạn chèn n phần tử vào Từ điển <>, nó sẽ không phải là O (1) do đang luyện lại.
Ilya Ryzhenkov

2
FYI, ngay cả khi luyện tập lại, Từ điển vẫn là O (1). Hãy xem xét kịch bản ngay trước khi Từ điển mở rộng. Một nửa các yếu tố - những yếu tố đã được thêm vào từ lần mở rộng cuối cùng - sẽ được băm một lần. Một nửa số còn lại sẽ được băm hai lần. Một nửa số còn lại từ đó, ba lần, v.v ... Số thao tác băm trung bình được thực hiện trên mỗi phần tử sẽ là 1 + 1/2 + 1/4 + 1/8 ... = 2. Tình huống ngay sau khi mở rộng về cơ bản là giống nhau, nhưng với mỗi phần tử đã được băm thêm một lần nữa (vì vậy số băm trung bình là ba). Tất cả các kịch bản khác là giữa những người.
supercat

3

Các bộ sưu tập chung sẽ hoạt động tốt hơn so với các đối tác không chung chung của chúng, đặc biệt là khi lặp qua nhiều mục. Điều này là do quyền anh và unboxing không còn xảy ra.


2

Một lưu ý quan trọng về Hashtable vs Dictionary cho kỹ thuật giao dịch có hệ thống tần số cao: Vấn đề an toàn theo chủ đề

Hashtable là luồng an toàn để sử dụng bởi nhiều luồng. Từ điển thành viên tĩnh là chủ đề an toàn, nhưng bất kỳ thành viên thể hiện không được đảm bảo là như vậy.

Vì vậy, Hashtable vẫn là lựa chọn 'tiêu chuẩn' trong vấn đề này.


Điều này đúng một phần. Các Hashtablelà an toàn để sử dụng chỉ với một nhà văn và nhiều độc giả đồng thời. Mặt khác, an toàn khi sử dụng Dictionaryvới nhiều người đọc miễn là nó không được sửa đổi đồng thời.
Bryan Menard

Chắc chắn rồi. Tuy nhiên, trong không gian giao dịch, chúng tôi đồng thời đọc từ dữ liệu thị trường trực tiếp và chạy các phân tích bao gồm các mục được nối thêm. Nó cũng phụ thuộc vào số lượng nhà giao dịch đang sử dụng hệ thống - nếu đó chỉ là bạn, rõ ràng điều đó không thành vấn đề.
Cướp

1
.NET 4.0 cung cấp một từ đồng thời <TKey, TValue>
Rob

1

Có sự khác biệt tinh tế và không quá tinh tế giữa các bộ sưu tập chung và không chung chung. Họ chỉ đơn thuần sử dụng các cấu trúc dữ liệu cơ bản khác nhau. Ví dụ, Hashtable đảm bảo một nhà văn-nhiều người đọc mà không cần đồng bộ hóa. Từ điển không.


1

Cấu trúc dữ liệu và bộ sưu tập C # phổ biến nhất

  • Mảng
  • Lập danh sách
  • Danh sách
  • Danh sách liên kết
  • Từ điển
  • Hashset
  • Cây rơm
  • Xếp hàng
  • Danh sách được sắp xếp

C # .NET có rất nhiều cấu trúc dữ liệu khác nhau, ví dụ, một trong những cấu trúc phổ biến nhất là Mảng. Tuy nhiên, C # đi kèm với nhiều cấu trúc dữ liệu cơ bản hơn. Chọn cấu trúc dữ liệu chính xác để sử dụng là một phần của việc viết một chương trình có cấu trúc tốt và hiệu quả.

Trong bài viết này tôi sẽ đi qua các cấu trúc dữ liệu C # tích hợp, bao gồm các cấu trúc dữ liệu mới được giới thiệu trong C # .NET 3.5. Lưu ý rằng nhiều cấu trúc dữ liệu này áp dụng cho các ngôn ngữ lập trình khác.

Mảng

Cấu trúc dữ liệu có lẽ đơn giản và phổ biến nhất là mảng. Mảng AC # về cơ bản là một danh sách các đối tượng. Đặc điểm xác định của nó là tất cả các đối tượng là cùng loại (trong hầu hết các trường hợp) và có một số lượng cụ thể của chúng. Bản chất của một mảng cho phép truy cập rất nhanh vào các phần tử dựa trên vị trí của chúng trong danh sách (còn được gọi là chỉ mục). Mảng AC # được định nghĩa như thế này:

[object type][] myArray = new [object type][number of elements]

Vài ví dụ:

 int[] myIntArray = new int[5];
 int[] myIntArray2 = { 0, 1, 2, 3, 4 };

Như bạn có thể thấy từ ví dụ trên, một mảng có thể được xác định nội bộ không có phần tử hoặc từ một tập hợp các giá trị hiện có. Chèn các giá trị vào một mảng là đơn giản miễn là chúng phù hợp. Hoạt động trở nên tốn kém khi có nhiều phần tử hơn kích thước của mảng, tại thời điểm đó, mảng cần được mở rộng. Điều này mất nhiều thời gian hơn vì tất cả các yếu tố hiện có phải được sao chép sang mảng mới, lớn hơn.

Lập danh sách

Cấu trúc dữ liệu C #, ArrayList, là một mảng động. Điều đó có nghĩa là một ArrayList có thể có bất kỳ số lượng đối tượng và loại nào. Cấu trúc dữ liệu này được thiết kế để đơn giản hóa các quy trình thêm các phần tử mới vào một mảng. Dưới mui xe, ArrayList là một mảng có kích thước được nhân đôi mỗi khi hết dung lượng. Nhân đôi kích thước của mảng bên trong là một chiến lược rất hiệu quả giúp giảm lượng sao chép phần tử trong thời gian dài. Chúng tôi sẽ không nhận được bằng chứng về điều đó ở đây. Cấu trúc dữ liệu rất đơn giản để sử dụng:

    ArrayList myArrayList = new ArrayList();
    myArrayList.Add(56);
    myArrayList.Add("String");
    myArrayList.Add(new Form());

Nhược điểm của cấu trúc dữ liệu ArrayList là người ta phải bỏ các giá trị bị loại bỏ trở lại kiểu ban đầu của chúng:

int arrayListValue = (int)myArrayList[0]

Nguồn và thông tin thêm bạn có thể tìm thấy ở đây :


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.