Sự khác biệt giữa Hashset <T> và Danh sách <T> là gì?


156

Bạn có thể giải thích sự khác biệt giữa HashSet<T>List<T>trong .NET là gì không?

Có lẽ bạn có thể giải thích với một ví dụ trong trường hợp nào HashSet<T>nên được ưu tiên chống lại List<T>?



Tôi khuyên bạn nên tham khảo các bài viết trên Wikipedia về en.wikipedia.org/wiki/Hash_tableen.wikipedia.org/wiki/Docate_array .
mqp

Để biết hiệu suất liên quan, hãy xem hashset-vs-list-Performance
nawfal

Câu trả lời:


213

Không giống như Danh sách <> ...

  1. Hashset là một danh sách không có thành viên trùng lặp.

  2. Do Hashset bị hạn chế chỉ chứa các mục duy nhất, nên cấu trúc bên trong được tối ưu hóa để tìm kiếm (so với danh sách) - nó nhanh hơn đáng kể

  3. Thêm vào Hashset trả về boolean - false nếu bổ sung không thành công do đã tồn tại trong Set

  4. Có thể thực hiện các hoạt động tập hợp toán học đối với Tập hợp: Liên kết / Giao lộ / IsSubsetOf, v.v.

  5. Hashset không triển khai ICollection chỉ IList

  6. Bạn không thể sử dụng các chỉ mục với Hashset, chỉ liệt kê.

Lý do chính để sử dụng Hashset sẽ là nếu bạn quan tâm đến việc thực hiện các thao tác Set.

Cho 2 bộ: hashIn1 và hashSet2

 //returns a list of distinct items in both sets
 HashSet set3 = set1.Union( set2 );

bay so với một hoạt động tương đương bằng cách sử dụng LINQ. Nó cũng gọn gàng hơn để viết!


IDK, tôi đã có vấn đề với Unionphương pháp. Tôi đã sử dụng UnionWiththay thế.
người dùng

2
+1 cho "Lý do chính để sử dụng HashingSet sẽ là nếu bạn quan tâm đến việc thực hiện các thao tác Cài đặt."
LCJ

12
Trên thực tế, tôi thích câu trả lời chỉ ra rằng HashSets phù hợp trong trường hợp bạn có thể coi bộ sưu tập của mình là "vật phẩm túi". Đặt thao tác không quá thường xuyên như Kiểm tra ngăn chặn. Tại bất kỳ thời điểm nào bạn có một bộ các mặt hàng duy nhất (ví dụ mã) và bạn cần kiểm tra ngăn chặn, Hashset rất tiện dụng.
ThunderGr

Câu trả lời tốt. Tôi cũng muốn thêm một vài khác biệt về hiệu suất.
nawfal

1
Câu hỏi: lý do chính là không có sự chắc chắn để không có các mục trùng lặp?
Andrea Scarafoni

54

Để chính xác hơn, hãy chứng minh bằng các ví dụ,

Bạn không thể sử dụng Hashset như trong ví dụ sau.

HashSet<string> hashSet1 = new HashSet<string>(){"1","2","3"};
for (int i = 0; i < hashSet1.Count; i++)
    Console.WriteLine(hashSet1[i]);

hashSet1[i] sẽ tạo ra một lỗi:

Không thể áp dụng lập chỉ mục với [] cho một biểu thức loại 'System.Collections.Generic.Hashset'

Bạn có thể sử dụng câu lệnh foreach:

foreach (var item in hashSet1)
    Console.WriteLine(item);

Bạn không thể thêm các mục trùng lặp vào Hashset trong khi Danh sách cho phép bạn thực hiện việc này và trong khi bạn thêm một mục vào Hashset, bạn có thể kiểm tra xem nó có chứa mục đó hay không.

HashSet<string> hashSet1 = new HashSet<string>(){"1","2","3"};
if (hashSet1.Add("1"))
   Console.WriteLine("'1' is successfully added to hashSet1!");
else
   Console.WriteLine("'1' could not be added to hashSet1, because it contains '1'");

HashSet có một số chức năng hữu ích như IntersectWith, UnionWith, IsProperSubsetOf, ExceptWith, SymmetricExceptWith, vv

IsProperSubsetOf:

HashSet<string> hashSet1 = new HashSet<string>() { "1", "2", "3", "4" };
HashSet<string> hashSet2 = new HashSet<string>() { "2", "4", "6", "8" };
HashSet<string> hashSet3 = new HashSet<string>() { "1", "2", "3", "4", "5" };
if (hashSet1.IsProperSubsetOf(hashSet3))
    Console.WriteLine("hashSet3 contains all elements of hashSet1.");
if (!hashSet1.IsProperSubsetOf(hashSet2))
    Console.WriteLine("hashSet2 does not contains all elements of hashSet1.");

UnionWith:

HashSet<string> hashSet1 = new HashSet<string>() { "3", "4" };
HashSet<string> hashSet2 = new HashSet<string>() { "2", "4", "6", "8" };
hashSet1.UnionWith(hashSet2); //hashSet1 -> 3, 2, 4, 6, 8

IntersectWith:

HashSet<string> hashSet1 = new HashSet<string>() { "3", "4", "8" };
HashSet<string> hashSet2 = new HashSet<string>() { "2", "4", "6", "8" }
hashSet1.IntersectWith(hashSet2);//hashSet1 -> 4, 8

ExceptWith :

 HashSet<string> hashSet1 = new HashSet<string>() { "1", "2", "3", "5", "6" };
 HashSet<string> hashSet2 = new HashSet<string>() { "1", "2", "3", "4" };
 hashSet1.ExceptWith(hashSet2);//hashSet1 -> 5, 6

SymmetricExceptWith :

 HashSet<string> hashSet1 = new HashSet<string>() { "1", "2", "3", "5", "6" };
 HashSet<string> hashSet2 = new HashSet<string>() { "1", "2", "3", "4" };
 hashSet1.SymmetricExceptWith(hashSet2);//hashSet1 -> 4, 5, 6

Nhân tiện, thứ tự không được bảo tồn trong HashSets. Trong ví dụ, chúng tôi đã thêm phần tử "2" cuối cùng nhưng nó theo thứ tự thứ hai:

HashSet<string> hashSet1 = new HashSet<string>() { "3", "4", "8" };
hashSet1.Add("1");    // 3, 4, 8, 1
hashSet1.Remove("4"); // 3, 8, 1
hashSet1.Add("2");    // 3, 2 ,8, 1

51

A HashSet<T>là một lớp được thiết kế để cung cấp cho bạn O(1)tra cứu ngăn chặn (nghĩa là bộ sưu tập này có chứa một đối tượng cụ thể không và cho tôi biết câu trả lời nhanh).

A List<T>là một lớp được thiết kế để cung cấp cho bạn một bộ sưu tập với O(1)quyền truy cập ngẫu nhiên hơn là có thể phát triển linh hoạt (nghĩ mảng động). Bạn có thể kiểm tra ngăn chặn O(n)kịp thời (trừ khi danh sách được sắp xếp, sau đó bạn có thể thực hiện tìm kiếm nhị phân O(log n)kịp thời).

Có lẽ bạn có thể giải thích với một ví dụ trong trường hợp nào HashSet<T>nên ưu tiên chống lạiList<T>

Khi bạn muốn kiểm tra ngăn chặn trong O(1).


Ngoại trừ O (log n) nếu danh sách được sắp xếp; Rốt cuộc, nó nhanh hơn tra cứu trong một danh sách chưa được sắp xếp.
Andrei

20

Sử dụng List<T>khi bạn muốn:

  • Lưu trữ một bộ sưu tập các mặt hàng theo một thứ tự nhất định.

Nếu bạn biết chỉ số của mục bạn muốn (chứ không phải giá trị của chính mục đó), truy xuất là O(1). Nếu bạn không biết chỉ mục, việc tìm kiếm mục sẽ mất nhiều thời gian hơn O(n)cho bộ sưu tập chưa được sắp xếp.

Sử dụng Hashset<T>khi bạn muốn:

  • Nhanh chóng tìm ra nếu một đối tượng nhất định được chứa trong một bộ sưu tập.

Nếu bạn biết tên của thứ bạn muốn tìm, Tra cứu là O(1)(đó là phần 'Hash'). Nó không duy trì một thứ tự như thế List<T>và bạn không thể lưu trữ các bản sao (thêm một bản sao không có tác dụng, đó là phần 'Đặt').

Một ví dụ về thời điểm sử dụng Hashset<T>sẽ là nếu bạn muốn tìm hiểu xem một từ được chơi trong trò chơi Scrabble có phải là một từ hợp lệ trong tiếng Anh (hoặc ngôn ngữ khác) hay không. Thậm chí sẽ tốt hơn nếu bạn muốn xây dựng một dịch vụ web được sử dụng bởi tất cả các phiên bản trực tuyến của một trò chơi như vậy.

A List<T>sẽ là một cấu trúc dữ liệu tốt để tạo bảng điểm để theo dõi điểm của người chơi.


15

Danh sách là một danh sách theo thứ tự. Nó là

  • được truy cập bởi một chỉ số nguyên
  • có thể chứa các bản sao
  • có một thứ tự dự đoán

Hashset là một bộ. Nó:

  • Có thể chặn các mục trùng lặp (xem Thêm (T) )
  • Không đảm bảo thứ tự của các mục trong bộ
  • Có các hoạt động bạn mong đợi trên một tập hợp, ví dụ: IntersectWith, IsProperSubsetOf, UnionWith.

Danh sách phù hợp hơn khi bạn muốn truy cập bộ sưu tập của mình như thể nó giống như một mảng mà bạn có thể chắp thêm, chèn và xóa các mục. Hashset là lựa chọn tốt hơn nếu bạn muốn coi bộ sưu tập của mình như một "túi" các mặt hàng trong đó thứ tự không quan trọng hoặc khi bạn muốn so sánh nó với các bộ khác bằng các thao tác như IntersectWith hoặc UnionWith.



3

Danh sách là một tập hợp các đối tượng của Loại T không theo thứ tự, không giống như một mảng bạn có thể thêm và xóa các mục.

Bạn sẽ sử dụng một danh sách mà bạn muốn tham chiếu các thành viên theo thứ tự bạn đã lưu trữ chúng và bạn đang truy cập chúng theo một vị trí chứ không phải chính mục đó.

Hashset giống như một cuốn từ điển mà chính vật phẩm là chìa khóa cũng như giá trị, việc đặt hàng không được đảm bảo.

Bạn sẽ sử dụng Hashset nơi bạn muốn kiểm tra xem một đối tượng có trong bộ sưu tập không


1
Để làm rõ, trong trường hợp bất kỳ ai khác đọc sai ngay từ cái nhìn đầu tiên - Listduy trì một trật tự (tức là khi mọi thứ được thêm vào), nhưng không tự động sắp xếp các mục. Bạn sẽ phải gọi .Sorthoặc sử dụng một SortedList.
drzaus

1

Nếu bạn quyết định áp dụng các cấu trúc dữ liệu này vào việc sử dụng thực tế trong phát triển dựa trên dữ liệu, Hashset rất hữu ích trong việc kiểm tra sao chép đối với các nguồn của bộ điều hợp dữ liệu, để làm sạch và di chuyển dữ liệu.

Ngoài ra, nếu sử dụng Lớp DataAnnotations, người ta có thể triển khai Logic khóa trên các thuộc tính của lớp và kiểm soát hiệu quả Chỉ mục tự nhiên (được nhóm hoặc không) với Hashset, trong đó việc này sẽ rất khó khăn trong việc triển khai Danh sách.

Tùy chọn mạnh mẽ để sử dụng danh sách là triển khai các tổng quát cho nhiều phương tiện trên Mô hình Chế độ xem, chẳng hạn như gửi danh sách các lớp tới Chế độ xem MVC cho Trình trợ giúp DropDownList và cũng để gửi dưới dạng cấu trúc JSON qua WebApi. Danh sách này cho phép logic tập hợp lớp điển hình và giữ tính linh hoạt cho cách tiếp cận "Giao diện" hơn như tính toán một mô hình khung nhìn duy nhất cho các phương tiện khác nhau.

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.