HashSet <T> so với Dictionary <K, V> thời gian tìm kiếm wrt để tìm xem một mục có tồn tại hay không


103
HashSet<T> t = new HashSet<T>();
// add 10 million items


Dictionary<K, V> t = new Dictionary<K, V>();
// add 10 million items.

.ContainsPhương pháp của ai sẽ trở lại nhanh hơn?

Chỉ cần làm rõ, yêu cầu của tôi là tôi có 10 triệu đối tượng (thực sự là chuỗi) mà tôi cần kiểm tra xem chúng có tồn tại trong cấu trúc dữ liệu hay không. Tôi sẽ KHÔNG BAO GIỜ lặp lại.


1
Bước 1: Xem liệu cả hai có làm điều tương tự không (trong trường hợp này, hai bộ sưu tập dành cho các mục đích khác nhau) Bước 2: Tham khảo tài liệu và xem bạn có cảm thấy hài lòng về độ phức tạp tiệm cận của chúng hay không. Bước 3: Nếu bạn cảm thấy cần phải lo lắng nhiều hơn, hãy tự đo lường và sau đó đặt câu hỏi đăng điểm chuẩn cùng với nó. Trong trường hợp của bạn, câu hỏi trở nên vô nghĩa trong bước đầu tiên.
nawfal

Câu trả lời:


153

Kiểm tra hiệu suất HashSet so với Danh sách và Từ điển, được lấy từ đây .

Thêm 1000000 đối tượng (mà không cần kiểm tra bản sao)

Chứa séc cho một nửa đối tượng của bộ sưu tập 10000

Loại bỏ một nửa các đối tượng của bộ sưu tập 10000


9
Phân tích tuyệt vời! Có vẻ như .Contains for Dictionary quá nhanh nên không có lợi ích gì khi sử dụng HashSet, trong trường hợp của OP.
EtherDragon

2
vâng, tôi đã có cùng một câu hỏi với OP. Tôi đã có một từ điển mà tôi đang sử dụng vì những lý do khác và muốn biết liệu tôi có được lợi khi đổi sang Hashset thay vì sử dụng ContainsKey hay không. Có vẻ như câu trả lời là không vì cả hai đều quá nhanh.
FistOfFury

4
Trái ngược với những gì mà các nhận xét trước đây dường như ngụ ý, vâng, bạn nên chuyển sang HashSet vì nó cung cấp cho bạn những gì bạn muốn: lưu trữ một tập giá trị (trái ngược với việc duy trì một số loại ánh xạ). Câu trả lời này chỉ ra rằng sẽ không có tác động tiêu cực đến hiệu suất so với Từ điển.
Francois Beaussier

Câu trả lời này KHÔNG cho bạn biết hiệu suất của HashSet và Từ điển so sánh như thế nào ... tất cả những gì nó cho bạn biết là cả hai đều nhanh hơn một Danh sách .. à ... vâng! Chắc chắn! HashSet có thể nhanh hơn gấp 3 lần và bạn sẽ không biết vì thử nghiệm có liên quan đã thu gọn cả hai thành "chúng tức thời ... so với một Danh sách ".
Brondahl

71

Tôi cho rằng ý bạn là Dictionary<TKey, TValue>trong trường hợp thứ hai? HashTablelà một lớp không chung chung.

Bạn nên chọn bộ sưu tập phù hợp với công việc dựa trên yêu cầu thực tế của bạn. Bạn có thực sự muốn ánh xạ mỗi khóa thành một giá trị không? Nếu vậy, hãy sử dụng Dictionary<,>. Nếu bạn chỉ quan tâm đến nó như một bộ, hãy sử dụng HashSet<>.

Tôi mong đợi HashSet<T>.ContainsDictionary<TKey, TValue>.ContainsKey(là các hoạt động có thể so sánh, giả sử bạn đang sử dụng từ điển của mình một cách hợp lý) về cơ bản hoạt động giống nhau - về cơ bản chúng đang sử dụng cùng một thuật toán. Tôi đoán rằng với các mục nhập Dictionary<,>lớn hơn, bạn sẽ có nhiều khả năng bị nổ bộ nhớ cache Dictionary<,>hơn so với với HashSet<>, nhưng tôi hy vọng điều đó không đáng kể so với nỗi đau của việc chọn sai kiểu dữ liệu đơn giản về những gì bạn cố gắng đạt được.


Có, ý tôi là Từ điển <TKey, TValue>. Tôi chỉ lo ngại về tìm kiếm sự tồn tại mục trong một cấu trúc dữ liệu, đó là tất cả .
halivingston

3
@halivingston Trong trường hợp đó, hãy sử dụng HashSet. Rõ ràng rằng đó tất cả những gì bạn cần.
Jon Skeet

2
Được rồi cảm ơn. Tôi thực sự có HashSet <TKey> ngay bây giờ và một bản sao của Dictionary <Tkey, TValue> cũng có trong bộ nhớ. Đầu tiên tôi .Contains trên HashSet, sau đó truy xuất giá trị trong Dictionary <TKey, TValue>. Hiện tại tôi có bộ nhớ vô hạn, nhưng tôi sợ rằng bộ nhớ của mình sẽ sớm bị hạn chế và nhóm của chúng tôi sẽ yêu cầu tôi xóa nội dung trùng lặp này trong bộ nhớ, lúc đó tôi sẽ buộc phải sử dụng Dictionary <TKey, TValue>.
halivingston

4
Bạn có biết Dictionary cũng có chức năng ContainsKey phải không? Tại sao bạn lại sao chép dữ liệu?
Blindy

8
Nếu bạn đã có dữ liệu trong từ điển, thì nhận xét đầu tiên của bạn rõ ràng là không chính xác - bạn cũng cần liên kết các khóa với các giá trị. Có thể không phải cho đoạn mã cụ thể này , nhưng điều đó không liên quan. Nếu bạn đã có một Dictionaryvì lý do khác, bạn nên sử dụng nó.
Jon Skeet

7

Từ tài liệu MSDN cho Dictionary <TKey, TValue>

"Việc truy xuất một giá trị bằng cách sử dụng khóa của nó rất nhanh, gần bằng O (1) , bởi vì lớp Từ điển được triển khai dưới dạng bảng băm. "

Với một lưu ý:

"Tốc độ truy xuất phụ thuộc vào chất lượng của thuật toán băm của loại được chỉ định cho TKey"

Tôi biết câu hỏi / bài đăng của bạn đã cũ - nhưng trong khi tìm kiếm câu trả lời cho một câu hỏi tương tự, tôi tình cờ gặp phải điều này.

Hi vọng điêu nay co ich. Cuộn xuống phần Nhận xét để biết thêm chi tiết. https://msdn.microsoft.com/en-us/library/xfhwa508(v=vs.110).aspx


4

Đây là các cấu trúc dữ liệu khác nhau. Ngoài ra không có phiên bản chung của HashTable.

HashSetchứa các giá trị kiểu T HashTable(hoặc Dictionary) chứa các cặp khóa-giá trị. Vì vậy, bạn nên chọn bộ sưu tập trên những dữ liệu bạn cần được lưu trữ.


0

Câu trả lời được chấp nhận cho câu hỏi này KHÔNG trả lời hợp lệ câu hỏi! Tình cờ đưa ra câu trả lời chính xác, nhưng câu trả lời đó không được hiển thị bằng bằng chứng họ cung cấp.

Câu trả lời đó cho thấy rằng việc tra cứu Key trên a Dictionaryhoặc HashSetnhanh hơn rất nhiều so với tra cứu trong a List. Điều đó đúng, nhưng không thú vị, cũng không ngạc nhiên, cũng không bằng chứng rằng chúng có cùng tốc độ.

Tôi đã chạy đoạn mã bên dưới để so sánh thời gian tra cứu và kết luận của tôi là chúng trên thực tế có cùng tốc độ. (Hoặc ít nhất, nếu có bất kỳ sự khác biệt nào, thì sự khác biệt đó nằm trong Độ lệch Chuẩn của tốc độ đó)

Cụ thể, 100.000.000 lần tra cứu mất từ ​​10 đến 11,5 giây cho cả hai, đối với tôi, trong thử nghiệm này.

Mã kiểm tra:

private const int TestReps = 100_000_000;
[Test]
public void CompareHashSetContainsVersusDictionaryContainsKey()
{
    for (int j = 0; j < 10; j++)
    {
        var rand = new Random();
        var dict = new Dictionary<int, int>();
        var hash = new HashSet<int>();

        for (int i = 0; i < TestReps; i++)
        {
            var key = rand.Next();
            var value = rand.Next();
            hash.Add(key);
            dict.TryAdd(key, value);
        }

        var testPoints = Enumerable.Repeat(1, TestReps).Select(_ => rand.Next()).ToArray();
        var timer = new Stopwatch();
        var total = 0;
        
        timer.Restart();
            for (int i = 0; i < TestReps; i++)
            {
                var newKey = testPoints[i];
                if (hash.Contains(newKey))
                {
                    total++;
                }
            }
        Console.WriteLine(timer.Elapsed);
        
        var target = total;
        Assert.That(total == target);
        

        timer.Restart();
            for (int i = 0; i < TestReps; i++)
            {
                var newKey = testPoints[i];
                if (dict.ContainsKey(newKey))
                {
                    total++;
                }
            }
        Console.WriteLine(timer.Elapsed);

        Assert.That(total == target * 2);
        Console.WriteLine("Set");
    }
}
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.