Sự khác biệt giữa Tra cứu () và Từ điển (Danh sách ())


165

Tôi đang cố gắng xoay quanh cấu trúc dữ liệu nào là hiệu quả nhất và khi nào / ở đâu để sử dụng cấu trúc nào.

Bây giờ, có thể là tôi chỉ đơn giản là không hiểu rõ các cấu trúc, nhưng làm thế nào ILookup(of key, ...)khác với một Dictionary(of key, list(of ...))?

Ngoài ra tôi muốn sử dụng ILookupở đâu và ở đâu sẽ hiệu quả hơn về tốc độ truy cập / bộ nhớ / dữ liệu chương trình, v.v.?


1
người ta cũng có thể muốn xem những gì-is-the-point-of-lookuptkey-
telement

Câu trả lời:


255

Hai sự khác biệt đáng kể:

  • Lookuplà bất biến. Yay :) (Ít nhất, tôi tin rằng Lookuplớp cụ thể là bất biến, và ILookupgiao diện không cung cấp bất kỳ thành viên đột biến nào. Dĩ nhiên, có thể có các triển khai đột biến khác.)
  • Khi bạn tra cứu một khóa không có trong tra cứu, bạn sẽ nhận được một chuỗi trống thay vì a KeyNotFoundException. (Do đó không có TryGetValue, AFAICR.)

Chúng có khả năng tương đương về hiệu quả - Dictionary<TKey, GroupingImplementation<TValue>>ví dụ, việc tra cứu cũng có thể sử dụng hậu trường. Chọn giữa chúng dựa trên yêu cầu của bạn. Cá nhân tôi thấy rằng việc tra cứu thường phù hợp hơn a Dictionary<TKey, List<TValue>>, chủ yếu là do hai điểm đầu tiên ở trên.

Lưu ý rằng là một chi tiết triển khai, việc triển khai cụ thể IGrouping<,>được sử dụng cho các giá trị thực hiện IList<TValue>, có nghĩa là nó hiệu quả để sử dụng Count(), ElementAt()v.v.


Nếu một tra cứu khóa không tồn tại dẫn đến một chuỗi trống thay vì một ngoại lệ, thì nó không thể được sử dụng như một bộ sưu tập mục đích chung. Sẽ không sao trong trường hợp bộ sưu tập không thay đổi, là sản phẩm phụ của các truy vấn linq.
nawfal

@nawfal - đó chính xác là những gì Tra cứu dành cho. Từ msdn : "Bạn có thể tạo một phiên bản Tra cứu <TKey, TEuity> bằng cách gọi ToLookup trên một đối tượng thực hiện IEnumerable <T>."
Niall Connaughton

50

Điều thú vị là không ai tuyên bố sự khác biệt lớn nhất thực tế (Lấy trực tiếp từ MSDN ):

Một Tra cứu giống như một từ điển. Sự khác biệt là Từ điển ánh xạ các khóa thành các giá trị đơn, trong khi Tra cứu ánh xạ các khóa thành các bộ sưu tập các giá trị.


51
Kiểm tra câu hỏi: đó là về sự khác biệt giữa Tra cứu <TKey, TValue> và Từ điển <TKey, Danh sách <TValue >>, vì vậy sự khác biệt đó là loại rõ ràng.
Martao

5
@Martao một số người tìm thấy câu hỏi này khi tìm hiểu sự khác biệt giữa tra cứu và từ điển. Câu trả lời này thực sự hữu ích.
jakubiszon

34

Cả a Dictionary<Key, List<Value>>Lookup<Key, Value>logic đều có thể giữ dữ liệu được sắp xếp theo cách tương tự và cả hai đều có cùng thứ tự hiệu quả. Sự khác biệt chính Lookuplà không thay đổi: nó không có Add()phương thức và không có hàm tạo công khai (và như Jon đã đề cập, bạn có thể truy vấn khóa không tồn tại mà không có ngoại lệ và có khóa là một phần của nhóm).

Bạn sử dụng cái nào, nó thực sự phụ thuộc vào cách bạn muốn sử dụng chúng. Nếu bạn đang duy trì một bản đồ khóa cho nhiều giá trị liên tục được sửa đổi, thì Dictionary<Key, List<Value>>có lẽ tốt hơn vì nó có thể thay đổi.

Tuy nhiên, nếu bạn có một chuỗi dữ liệu và chỉ muốn một chế độ xem chỉ đọc dữ liệu được sắp xếp theo khóa, thì việc tra cứu rất dễ dàng để xây dựng và sẽ cung cấp cho bạn ảnh chụp nhanh chỉ đọc.


11

Sự khác biệt chính giữa an ILookup<K,V>và a Dictionary<K, List<V>>là từ điển có thể thay đổi; bạn có thể thêm hoặc xóa các khóa và cũng có thể thêm hoặc xóa các mục khỏi danh sách được tra cứu. An ILookupbất biến và không thể sửa đổi một khi được tạo.

Việc triển khai cơ bản của cả hai cơ chế sẽ giống nhau hoặc tương tự nhau, do đó tốc độ tìm kiếm và dấu chân bộ nhớ của chúng sẽ xấp xỉ nhau.


1
@JohnBustos Về hiệu suất, không. Nó hoàn toàn hợp lý. Bạn có thể chuyển các tham chiếu đến cấu trúc xung quanh mà không phải lo lắng về việc người khác sửa đổi nó từ bên dưới bạn. Bạn có thể đưa ra các giả định về thực tế rằng nó không thể thay đổi nếu bạn không thể thay đổi.
Phục vụ

Cảm ơn, Servy, đó là một điểm rất tốt khi bạn thường xuyên đi qua rất nhiều biến ByRef - Ít nhất là biến này bạn chắc chắn không thể sửa đổi. Cảm ơn!
John Bustos

2
@JohnBustos Hãy nhớ rằng phương thức truyền tham số phương thức mặc định là theo giá trị, bạn cần thêm byref một cách rõ ràng và đó là điều nên được thực hiện khá hiếm. Các cấu trúc dữ liệu này là các lớp, làm cho chúng trở thành các kiểu tham chiếu, vì vậy việc chuyển giá trị là giá trị của tham chiếu, đó là lý do tại sao việc chuyển nó sang phương thức khác có thể gây ra các thay đổi có thể nhìn thấy cho người gọi.
Phục vụ

Cảm ơn, Servy, đã mở ra một hộp giun hoàn toàn mới cho tôi về những gì tôi đã làm :), nhưng tôi hiểu những gì bạn đang nói. Cảm ơn!!
John Bustos

Dưới vỏ bọc bạn có biết Tra cứu có sử dụng hashbuckets cho khóa không?
paparazzo

10

Một sự khác biệt khác chưa được đề cập là Tra cứu () hỗ trợ các khóa null :

Lớp tra cứu thực hiện giao diện ILookup. Tra cứu rất giống với một từ điển ngoại trừ nhiều giá trị được phép ánh xạ tới cùng một khóa và các khóa null được hỗ trợ.


4

Khi ngoại lệ không phải là một tùy chọn, hãy đi Tra cứu

Nếu bạn đang cố gắng để có được một cấu trúc hiệu quả như một Dictionarynhưng bạn không biết chắc chắn rằng không có khóa trùng lặp trong đầu vào, Lookupthì an toàn hơn.

Như đã đề cập trong một câu trả lời khác, nó cũng hỗ trợ các khóa null và luôn trả về kết quả hợp lệ khi được truy vấn với dữ liệu tùy ý, do đó, nó có vẻ linh hoạt hơn với đầu vào không xác định (ít bị hơn so với Từ điển để đưa ra ngoại lệ).

Và nó đặc biệt đúng nếu bạn so sánh nó với System.Linq.Enumerable.ToDictionaryhàm:

// won't throw
new[] { 1, 1 }.ToLookup(x => x); 

// System.ArgumentException: An item with the same key has already been added.
new[] { 1, 1 }.ToDictionary(x => x);

Thay thế sẽ là viết mã quản lý khóa trùng lặp của riêng bạn bên trong một foreachvòng lặp.

Cân nhắc hiệu suất, Từ điển: một người chiến thắng rõ ràng

Nếu bạn không cần một danh sách và bạn sẽ quản lý một số lượng lớn các mặt hàng, Dictionary(hoặc thậm chí cấu trúc tùy chỉnh của riêng bạn) sẽ hiệu quả hơn:

        Stopwatch stopwatch = new Stopwatch();
        var list = new List<string>();
        for (int i = 0; i < 5000000; ++i)
        {
            list.Add(i.ToString());
        }
        stopwatch.Start();
        var lookup = list.ToLookup(x => x);
        stopwatch.Stop();
        Console.WriteLine("Creation: " + stopwatch.Elapsed);

        // ... Same but for ToDictionary
        var lookup = list.ToDictionary(x => x);
        // ...

Lookupphải duy trì một danh sách các mục cho mỗi khóa, nó chậm hơn Từ điển (chậm hơn khoảng 3 lần đối với số lượng lớn các mục)

Tốc độ tra cứu: Tạo: 00: 00: 01.5760444

Tốc độ từ điển: Tạo: 00: 00: 00.4418833

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.