ILookup <TKey, TVal> so với IGrouping <TKey, TVal>


76

Tôi đã gặp khó khăn khi nói rõ sự khác biệt giữa ILookup<TKey, TVal>IGrouping<TKey, TVal>, và tôi tò mò liệu bây giờ tôi có hiểu đúng về nó không. LINQ đã làm phức tạp vấn đề bằng cách tạo chuỗi các IGroupingmục đồng thời cung cấp cho tôi một ToLookupphương pháp mở rộng. Vì vậy, tôi cảm thấy chúng giống nhau cho đến khi tôi nhìn kỹ hơn.

var q1 = 
    from n in N
    group n by n.MyKey into g
    select g;
// q1 is IEnumerable<IGrouping<TKey, TVal>>

Tương đương với:

var q2 = N.GroupBy(n => n.MyKey, n => n);
// q2 is IEnumerable<IGrouping<TKey, TVal>>

Trông rất giống:

var q3 = N.ToLookup(n => n.MyKey, n => n);
// q3 is ILookup<TKey, TVal>

Tôi có đúng trong các phép loại suy sau đây không?

  1. An IGrouping<TKey, TVal>là một nhóm duy nhất (tức là một chuỗi có khóa), tương tự với KeyValuePair<TKey, TVal>nơi giá trị thực sự là một chuỗi các phần tử (thay vì một phần tử)
  2. An IEnumerable<IGrouping<TKey, TVal>>là một chuỗi các chuỗi đó (tương tự như những gì bạn nhận được khi lặp lạiIDictionary<TKey, TVal>
  3. An ILookup<TKey, TVal>giống như một IDictionary<TKey, TVal>trong đó giá trị thực sự là một chuỗi các phần tử

Câu trả lời:


76

Vâng, tất cả những điều đó đều đúng.

ILookup<TKey, TValue>cũng mở rộng IEnumerable<IGrouping<TKey, TValue>>để bạn có thể lặp lại trên tất cả các cặp khóa / bộ sưu tập cũng như (hoặc thay vì) chỉ tìm kiếm các khóa cụ thể.

Về cơ bản tôi nghĩ ILookup<TKey,TValue>là như thế nào IDictionary<TKey, IEnumerable<TValue>>.

Hãy nhớ rằng đó ToLookuplà hoạt động "làm ngay bây giờ" (thực hiện ngay lập tức) trong khi a GroupByđược hoãn lại. Khi nó xảy ra, với cách hoạt động của "pull LINQ", khi bạn bắt đầu kéo IGroupings từ kết quả của a GroupBy, nó vẫn phải đọc tất cả dữ liệu (vì bạn không thể chuyển nhóm giữa chừng) trong khi trong các triển khai khác, nó có thể có thể tạo ra kết quả phát trực tuyến. (Điều này xảy ra trong Push LINQ; Tôi mong đợi LINQ với Sự kiện cũng vậy.)


Cảm ơn vi đa trả lơi. Tôi chưa từng nghĩ về Linq về push / pull trước đây. Khi tôi truy cập Google, tôi đã xem qua một trong các blog của bạn nên tôi sẽ kiểm tra. Nghe có vẻ là một cách thú vị để nghĩ về nó.
mckamey

Tôi vẫn nghĩ rằng những khác biệt này không biện minh cho 2 giao diện khác nhau. Tôi nghĩ rằng người thiết kế Thư viện nên quyết định chỉ một giao diện. Có thể chỉ là một sở thích cá nhân mà ở đây là một cái gì đó không đủ rõ ràng.
rudimenter

Đó là tôi hay có sự nhầm lẫn nào đó giữa GroupBy và GroupJoin? Chúng chỉ liên quan đến khái niệm . Có lẽ nó chỉ là một lỗi đánh máy trong câu trả lời.
sehe

8

Có một điểm khác biệt quan trọng giữa ILookup và IDictionary: ILookup trước đây thực thi tính bất biến theo nghĩa là ở đây không có phương pháp nào để thay đổi dữ liệu (ngoại trừ khi người tiêu dùng thực hiện một phép truyền rõ ràng). Ngược lại, IDictionary có các phương thức như "Thêm" cho phép thay đổi dữ liệu. Vì vậy, từ quan điểm của lập trình chức năng và / hoặc lập trình song song, ILookup là tốt hơn. (Tôi chỉ ước có một phiên bản ILookup chỉ gán một giá trị cho một khóa chứ không phải một nhóm.)

(Btw., Có vẻ như đáng chỉ ra rằng mối quan hệ giữa IEnumerable và IList hơi giống với mối quan hệ giữa ILookup và IDictionary - mối quan hệ trước là bất biến, mối quan hệ sau thì không.)


4
Tại sao bạn muốn một ILookupchỉ có giá trị một mục thay vì một nhóm? Đó là những gì a Dictionarylà - hoặc a ReadOnlyDictionaryhoặc IReadOnlyDictionarynếu bạn muốn nó là bất biến.
ErikE

5

GroupByToLookUpcó gần như cùng một chức năng TRỪ này: Reference

GroupBy: Toán tử GroupBy trả về các nhóm phần tử dựa trên một số giá trị khóa. Mỗi nhóm được đại diện bởi đối tượng IGrouping.

ToLookup: ToLookup cũng giống như GroupBy; sự khác biệt duy nhất là việc thực thi GroupBy bị hoãn lại trong khi việc thực thi ToLookup là ngay lập tức.

Hãy xóa sự khác biệt bằng cách sử dụng mã mẫu. giả sử rằng chúng ta có một Personmô hình đại diện cho lớp :

class Personnel
{
    public int Id { get; set; }
    public string FullName { get; set; }
    public int Level { get; set; }
}

sau đó, chúng tôi xác định một danh sách personnelsnhư sau:

 var personnels = new List<Personnel>
    {
        new Personnel { Id = 1, FullName = "P1", Level = 1 },
        new Personnel { Id = 2, FullName = "P2", Level = 2 },
        new Personnel { Id = 3, FullName = "P3", Level = 1 },
        new Personnel { Id = 4, FullName = "P4", Level = 1 },
        new Personnel { Id = 5, FullName = "P5", Level =2 },
        new Personnel { Id = 6, FullName = "P6", Level = 2 },
        new Personnel { Id = 7, FullName = "P7", Level = 2 }
    };

Bây giờ tôi cần personnelsnhóm theo cấp độ của họ. Tôi có hai cách tiếp cận ở đây. sử dụng GroupByhoặc ToLookUp. Nếu tôi sử dụng GroupBy, như đã nêu trước đây, nó sẽ sử dụng thực thi hoãn lại, điều này có nghĩa là khi bạn lặp lại qua bộ sưu tập, mục tiếp theo có thể được tính hoặc không cho đến khi nó được gọi.

 var groups = personnels.GroupBy(p => p.Level);
    personnels.RemoveAll(p => p.Level == 1);
    foreach (var product in groups)
    {
        Console.WriteLine(product.Key);
        foreach (var item in product)
            Console.WriteLine(item.Id + " >>> " + item.FullName + " >>> " + item.Level);
    }

Trong đoạn mã trên, trước tiên tôi đã nhóm lại personnels, nhưng trước khi lặp lại, tôi đã loại bỏ một số personnels. Vì GroupBysử dụng thực thi hoãn lại, do đó, kết quả cuối cùng sẽ không bao gồm các mục bị loại bỏ, bởi vì việc nhóm sẽ được tính toán ở foreachđiểm ở đây.

Đầu ra:

2
2 >>> P2 >>> 2
5 >>> P5 >>> 2
6 >>> P6 >>> 2
7 >>> P7 >>> 2

Nhưng nếu tôi viết lại mã trên như bên dưới: (lưu ý rằng mã giống với mã trước đó ngoại trừ GroupByđược thay thế bằng ToLookUp)

 var groups = personnels.ToLookup(p => p.Level);
    personnels.RemoveAll(p => p.Level == 1);
    foreach (var product in groups)
    {
        Console.WriteLine(product.Key);
        foreach (var item in product)
            Console.WriteLine(item.Id + " >>> " + item.FullName + " >>> " + item.Level);
    }

ToLookUpsử dụng thực thi ngay lập tức, điều đó có nghĩa là khi tôi gọi ToLookUpphương thức, kết quả sẽ được tạo và áp dụng nhóm, vì vậy nếu tôi xóa bất kỳ mục nào khỏi personnelslần lặp lại, điều đó sẽ không ảnh hưởng đến kết quả cuối cùng.

Đầu ra:

1
1 >>> P1 >>> 1
3 >>> P3 >>> 1
4 >>> P4 >>> 1
2
2 >>> P2 >>> 2
5 >>> P5 >>> 2
6 >>> P6 >>> 2
7 >>> P7 >>> 2

Lưu ý: GroupByToLookUpcả hai cũng trả về các kiểu khác nhau.

Bạn có thể sử dụng ToDictionary thay vì ToLookUp, nhưng bạn cần chú ý điều này :( tham khảo )

Việc sử dụng ToLookup () rất giống với ToDictionary (), cả hai đều cho phép bạn chỉ định bộ chọn khóa, bộ chọn giá trị và bộ so sánh. Sự khác biệt chính là ToLookup () cho phép (và mong đợi) các khóa trùng lặp trong khi ToDictionary () thì không


﹢1 cho ví dụ.
snr
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.