C# Enumerable類 之 數據分組

總目錄


前言

在 C# 中,System.Linq.Enumerable 類是 LINQ(Language Integrated Query)的核心組成部分,它提供了一系列靜態方法,用于操作實現了 IEnumerable 接口的集合。通過這些方法,我們可以輕松地對集合進行查詢、轉換、排序和聚合等操作。

本文屬于 C# Enumerable類 使用詳解 中的一個章節,著重介紹 C# Enumerable 類中數據分組這部分的內容。


一、概覽

方法描述示例
GroupBy數據分組people.GroupBy(p => p.Age);
ToLookup 數據分組people.ToLookup(p => p.Age);

二、GroupBy :數據分組

1. 什么是 GroupBy

GroupBy 是 LINQ 提供的一個擴展方法,用于將源集合中的元素按某個鍵值進行分組,并返回一個包含 IGrouping<TKey, TElement> 對象的集合。每個 IGrouping<TKey, TElement> 對象都表示一個組,其中包含該組的鍵和屬于該組的所有元素。

2. GroupBy 方法 基本信息

GroupBy 方法用于根據指定的鍵對集合中的元素進行分組。

1) GroupBy

public static IEnumerable<IGrouping<TKey, TSource>> GroupBy<TSource, TKey>(this IEnumerable<TSource> source,Func<TSource, TKey> keySelector)public static IEnumerable<IGrouping<TKey, TElement>> GroupBy<TSource, TKey, TElement>(this IEnumerable<TSource> source,Func<TSource, TKey> keySelector,Func<TSource, TElement> elementSelector)public static IEnumerable<TResult> GroupBy<TSource, TKey, TResult>(this IEnumerable<TSource> source,Func<TSource, TKey> keySelector,Func<TKey, IEnumerable<TSource>, TResult> resultSelector)
  • 參數

    • source:要分組的源集合。

    • keySelector:一個函數,用于從源集合的每個元素中提取分組鍵。

    • elementSelector(可選):一個函數,用于從源集合的每個元素中選擇要包含在組中的元素。

      • 指定組內元素的映射方式(默認映射原元素)
    • resultSelector(可選):一個函數,用于定義如何將每個組轉換為最終結果。

      • 自定義分組結果的輸出格式
  • 返回值GroupBy 方法返回一個 IEnumerable<IGrouping<TKey, TElement>> 集合,其中:

    • TKey:表示分組依據的鍵類型。
    • TElement:表示原始集合中的元素類型。

2)工作原理

  1. 迭代源集合
    GroupBy 方法首先會迭代源集合中的每一個元素。對于每個元素,它會調用 keySelector 函數來提取分組鍵。

  2. 創建組
    根據提取的鍵值,GroupBy 方法會創建或找到相應的組。如果某個鍵值對應的組已經存在,則將當前元素添加到該組中;如果不存在,則創建一個新的組并將當前元素添加進去。

  3. 返回結果
    在遍歷完所有元素后,GroupBy 方法返回一個包含所有組的集合。每個組都是一個 IGrouping<TKey, TElement> 對象,包含了鍵值和該組的所有元素。

  4. 惰性求值
    GroupBy 方法采用惰性求值(Lazy Evaluation),這意味著它不會立即執行分組操作,而是等到實際遍歷時才進行計算。這使得 GroupBy 可以處理無限序列或延遲執行復雜的查詢。

3)使用場景

  • 數據分類:當需要根據某個屬性或多個屬性對數據進行分類時。
  • 數據統計:當需要計算每個類別的統計信息(如計數、總和、平均值等)時。
  • 復雜的查詢和分析:當需要對數據進行復雜的查詢和分析時。

3. 使用示例

示例 1:基本分組

假設我們有一個包含若干 Person 對象的列表,每個對象都有 NameAge 屬性。我們希望根據 Age 屬性對這些對象進行分組。

class Person
{public string Name { get; set; }public int Age { get; set; }public override string ToString(){return $"{Name} ({Age})";}
}class Program
{static void Main(){List<Person> people = new List<Person>{new Person { Name = "Alice", Age = 30 },new Person { Name = "Bob", Age = 25 },new Person { Name = "Charlie", Age = 30 },new Person { Name = "David", Age = 25 }};var groupedPeople = people.GroupBy(p => p.Age);foreach (var group in groupedPeople){Console.WriteLine($"Age: {group.Key}");foreach (var person in group){Console.WriteLine($"  {person}");}}}
}

輸出結果

Age: 30Alice (30)Charlie (30)
Age: 25Bob (25)David (25)

在這個例子中,我們使用 GroupBy 方法根據 Age 屬性對 people 列表進行了分組,并打印了每個組及其成員。

示例 2:選擇特定元素

有時我們只關心某些特定的屬性,而不是整個對象。在這種情況下,可以使用 elementSelector 參數來選擇要包含在組中的元素。

var groupedNames = people.GroupBy(p => p.Age, p => p.Name);foreach (var group in groupedNames)
{Console.WriteLine($"Age: {group.Key}");foreach (var name in group){Console.WriteLine($"  {name}");}
}

輸出結果

Age: 30AliceCharlie
Age: 25BobDavid

在這個例子中,我們只選擇了 Name 屬性進行分組,并打印了每個組的名稱。

示例 3:投影分組結果

我們可以使用 resultSelector 參數來自定義分組后的結果。例如,我們可以計算每個年齡段的人數。

var ageCounts = people.GroupBy(p => p.Age,(age, persons) => new { Age = age, Count = persons.Count() }
);foreach (var ageCount in ageCounts)
{Console.WriteLine($"Age: {ageCount.Age}, Count: {ageCount.Count}");
}

輸出結果

Age: 30, Count: 2
Age: 25, Count: 2

在這個例子中,我們使用 resultSelector 參數創建了一個匿名對象,其中包含每個年齡段的人數。

示例 4:與其他LINQ方法結合

GroupBy 方法還可以與其他 LINQ 方法結合使用,以實現更復雜的數據處理。例如,我們可以使用 Select 方法來投影每個組的結果:

public class Student
{public string Name { get; set; }public int Grade { get; set; }
}public class Program
{public static void Main(){List<Student> students = new List<Student>{new Student { Name = "Alice", Grade = 10 },new Student { Name = "Bob", Grade = 11 },new Student { Name = "Charlie", Grade = 10 },new Student { Name = "David", Grade = 12 },new Student { Name = "Eve", Grade = 11 }};var summary = students.GroupBy(student => student.Grade).Select(group => new{Grade = group.Key,Count = group.Count(),Names = string.Join(", ", group.Select(s => s.Name))});foreach (var item in summary){Console.WriteLine($"Grade {item.Grade} has {item.Count} students: {item.Names}");}}
}

輸出結果:

Grade 10 has 2 students: Alice, Charlie
Grade 11 has 2 students: Bob, Eve
Grade 12 has 1 students: David

示例4 和 示例5 效果是一樣的,只不過示例4 是利用GroupBy 的一個重載方法實現,而示例5 是結合Select 方法實現的。

示例 5:多列分組

有時我們需要根據多個屬性進行分組。可以通過組合多個屬性來創建復合鍵。

var groupedByAgeAndName = people.GroupBy(p => new { p.Age, p.Name });foreach (var group in groupedByAgeAndName)
{Console.WriteLine($"Age: {group.Key.Age}, Name: {group.Key.Name}");foreach (var person in group){Console.WriteLine($"  {person}");}
}

輸出結果

Age: 30, Name: AliceAlice (30)
Age: 25, Name: BobBob (25)
Age: 30, Name: CharlieCharlie (30)
Age: 25, Name: DavidDavid (25)

在這個例子中,我們根據 AgeName 屬性創建了一個復合鍵,并對 people 列表進行了分組。

示例 6:使用自定義比較器

默認情況下,GroupBy 方法使用默認的相等比較器來比較鍵。如果需要自定義比較邏輯,可以傳遞一個 IEqualityComparer<TKey> 實現。

class Person
{public string Name { get; set; }public int Age { get; set; }public override string ToString(){return $"{Name} ({Age})";}
}class CustomComparer : IEqualityComparer<int>
{public bool Equals(int x, int y){// 自定義比較邏輯:年齡相差不超過1歲視為相同return Math.Abs(x - y) <= 1;}public int GetHashCode(int obj){return obj.GetHashCode();}
}class Program
{static void Main(){List<Person> people = new List<Person>{new Person { Name = "Alice", Age = 30 },new Person { Name = "Bob", Age = 25 },new Person { Name = "Charlie", Age = 30 },new Person { Name = "David", Age = 26 }};// 使用自定義比較器進行分組var customGroupedPeople = people.GroupBy(p => p.Age, new CustomComparer());foreach (var group in customGroupedPeople){Console.WriteLine($"Age: {group.Key}");foreach (var person in group){Console.WriteLine($"  {person}");}}}
}

輸出結果

Age: 30Alice (30)Charlie (30)
Age: 25Bob (25)
Age: 26David (26)

在這個例子中,我們使用了一個自定義的比較器來分組年齡相差不超過1歲的人員。實際上并沒有生效

示例 6:合并多個集合

有時我們需要合并多個集合并對合并后的結果進行分組。可以使用 ConcatUnion 方法先合并集合,然后再使用 GroupBy 進行分組。

class Person
{public string Name { get; set; }public int Age { get; set; }public override string ToString(){return $"{Name} ({Age})";}
}class Program
{static void Main(){List<Person> people = new List<Person>{new Person { Name = "Alice", Age = 30 },new Person { Name = "Bob", Age = 25 }};List<Person> morePeople = new List<Person>{new Person { Name = "Charlie", Age = 30 },new Person { Name = "David", Age = 40 }};// 合并兩個集合var combinedPeople = people.Concat(morePeople);// 使用 GroupBy 對合并后的集合進行分組var groupedCombinedPeople = combinedPeople.GroupBy(p => p.Age);foreach (var group in groupedCombinedPeople){Console.WriteLine($"Age: {group.Key}");foreach (var person in group){Console.WriteLine($"  {person}");}}}
}

輸出結果

Age: 30Alice (30)Charlie (30)
Age: 25Bob (25)
Age: 40David (40)

4. 注意事項

  • GroupBy 方法是一個延遲執行的操作,這意味著它不會立即執行分組操作,而是直到結果被枚舉時才會執行。
  • 分組鍵必須是可比較的,否則會引發異常。
  • 如果源集合為空,GroupBy 方法將返回一個空的集合。

三、ToLookup :數據分組

1. 什么是 ToLookup

ToLookup 是 LINQ 提供的一個擴展方法,用于根據指定的鍵對集合中的元素進行分組,并返回一個 ILookup<TKey, TElement> 對象。每個 ILookup<TKey, TElement> 對象都表示一個鍵到多個元素的映射。

2. ToLookup 方法 基本信息

ToLookup 方法用于根據指定的鍵對集合中的元素進行分組,并返回一個 ILookup<TKey, TElement> 對象。

1) ToLookup

ToLookup 方法有多種重載形式,以下是幾種常見的簽名:

public static ILookup<TKey, TSource> ToLookup<TSource, TKey>(this IEnumerable<TSource> source,Func<TSource, TKey> keySelector
)public static ILookup<TKey, TElement> ToLookup<TSource, TKey, TElement>(this IEnumerable<TSource> source,Func<TSource, TKey> keySelector,Func<TSource, TElement> elementSelector
)public static ILookup<TKey, TSource> ToLookup<TSource, TKey>(this IEnumerable<TSource> source,Func<TSource, TKey> keySelector,IEqualityComparer<TKey> comparer
)public static ILookup<TKey, TElement> ToLookup<TSource, TKey, TElement>(this IEnumerable<TSource> source,Func<TSource, TKey> keySelector,Func<TSource, TElement> elementSelector,IEqualityComparer<TKey> comparer
)
  • 參數

    • source:要轉換為 ILookup 的源集合。
    • keySelector:一個函數,用于從源集合的每個元素中提取分組鍵。
    • elementSelector(可選):一個函數,用于從源集合的每個元素中選擇要包含在組中的元素。
    • comparer(可選):一個 IEqualityComparer<TKey> 實現,用于比較鍵值。
  • 返回類型ToLookup 方法返回一個 ILookup<TKey, TElement> 集合,其中:

    • TKey:表示分組依據的鍵類型。
    • TElement:表示原始集合中的元素類型。
    • 每個 ILookup<TKey, TElement> 對象都包含一個鍵和屬于該鍵的所有元素。

2)工作原理

  1. 即時求值
    與 GroupBy 的惰性求值不同,ToLookup 是立即執行的。這意味著它會立即遍歷整個源集合并構建結果。這使得 ToLookup 更適合需要頻繁查詢且希望避免重復執行分組操作的場景。

  2. 創建組
    ToLookup 方法首先會迭代源集合中的每一個元素。對于每個元素,它會調用 keySelector 函數來提取分組鍵。然后根據提取的鍵值創建或找到相應的組。

  3. 添加元素
    根據提取的鍵值,ToLookup 方法會將當前元素添加到對應的組中。如果某個鍵值對應的組已經存在,則將當前元素添加到該組中;如果不存在,則創建一個新的組并將當前元素添加進去。

  4. 返回結果
    在遍歷完所有元素后,ToLookup 方法返回一個 ILookup<TKey, TElement> 對象,該對象包含了所有分組后的結果。由于 ILookup<TKey, TElement> 是只讀的,一旦創建就不能修改。

  5. 多次查詢
    由于 ILookup<TKey, TElement> 是預先計算好的,因此可以多次查詢而不會重復執行分組操作。這對于需要頻繁訪問分組結果的應用場景非常有用。

3)使用場景

  • 數據分類:當需要根據某個屬性或多個屬性對數據進行分類時。
  • 頻繁查詢分組結果:當需要頻繁查詢分組結果且希望避免重復執行分組操作時。
  • 不可變的分組結果:當需要一個不可變的分組結果時。

3. 使用示例

示例 1:基本分組

假設我們有一個包含若干 Person 對象的列表,每個對象都有 NameAge 屬性。我們希望根據 Age 屬性對這些對象進行分組。

class Person
{public string Name { get; set; }public int Age { get; set; }public override string ToString(){return $"{Name} ({Age})";}
}class Program
{static void Main(){List<Person> people = new List<Person>{new Person { Name = "Alice", Age = 30 },new Person { Name = "Bob", Age = 25 },new Person { Name = "Charlie", Age = 30 },new Person { Name = "David", Age = 25 }};// 使用 ToLookup 根據 Age 屬性進行分組var lookupPeople = people.ToLookup(p => p.Age);foreach (var group in lookupPeople){Console.WriteLine($"Age: {group.Key}");foreach (var person in group){Console.WriteLine($"  {person}");}}}
}

輸出結果

Age: 30Alice (30)Charlie (30)
Age: 25Bob (25)David (25)

在這個例子中,我們使用 ToLookup 方法根據 Age 屬性對 people 列表進行了分組,并打印了每個組及其成員。

示例 2:選擇特定元素

有時我們只關心某些特定的屬性,而不是整個對象。在這種情況下,可以使用 elementSelector 參數來選擇要包含在組中的元素。

class Person
{public string Name { get; set; }public int Age { get; set; }public override string ToString(){return $"{Name} ({Age})";}
}class Program
{static void Main(){List<Person> people = new List<Person>{new Person { Name = "Alice", Age = 30 },new Person { Name = "Bob", Age = 25 },new Person { Name = "Charlie", Age = 30 },new Person { Name = "David", Age = 25 }};// 使用 ToLookup 并選擇特定元素(僅選擇 Name)var lookupNames = people.ToLookup(p => p.Age, p => p.Name);foreach (var group in lookupNames){Console.WriteLine($"Age: {group.Key}");foreach (var name in group){Console.WriteLine($"  {name}");}}}
}

輸出結果

Age: 30AliceCharlie
Age: 25BobDavid

在這個例子中,我們只選擇了 Name 屬性進行分組,并打印了每個組的名稱。

示例 3:自定義比較器

默認情況下,ToLookup 方法使用默認的相等比較器來比較鍵。如果需要自定義比較邏輯,可以傳遞一個 IEqualityComparer<TKey> 實現。

class Person
{public string Name { get; set; }public int Age { get; set; }public override string ToString(){return $"{Name} ({Age})";}
}class CustomComparer : IEqualityComparer<int>
{public bool Equals(int x, int y){// 自定義比較邏輯:年齡相差不超過1歲視為相同return Math.Abs(x - y) <= 1;}public int GetHashCode(int obj){return obj.GetHashCode();}
}class Program
{static void Main(){List<Person> people = new List<Person>{new Person { Name = "Alice", Age = 30 },new Person { Name = "Bob", Age = 25 },new Person { Name = "Charlie", Age = 30 },new Person { Name = "David", Age = 26 }};// 使用自定義比較器進行分組var customLookupPeople = people.ToLookup(p => p.Age, new CustomComparer());foreach (var group in customLookupPeople){Console.WriteLine($"Age: {group.Key}");foreach (var person in group){Console.WriteLine($"  {person}");}}}
}

輸出結果:

Age: 30Alice (30)Charlie (30)
Age: 25Bob (25)
Age: 26David (26)

在這個例子中,我們使用了一個自定義的比較器來分組年齡相差不超過1歲的人員。實際上并沒有生效

示例 4:多列分組

class Person
{public string Name { get; set; }public int Age { get; set; }public override string ToString(){return $"{Name} ({Age})";}
}class Program
{static void Main(){List<Person> people = new List<Person>{new Person { Name = "Alice", Age = 30 },new Person { Name = "Bob", Age = 25 },new Person { Name = "Charlie", Age = 30 },new Person { Name = "David", Age = 25 }};// 使用 ToLookup 進行多列分組(根據 Age 和 Name)var lookupByAgeAndName = people.ToLookup(p => new { p.Age, p.Name });foreach (var group in lookupByAgeAndName){Console.WriteLine($"Age: {group.Key.Age}, Name: {group.Key.Name}");foreach (var person in group){Console.WriteLine($"  {person}");}}}
}

輸出結果:

Age: 30, Name: AliceAlice (30)
Age: 25, Name: BobBob (25)
Age: 30, Name: CharlieCharlie (30)
Age: 25, Name: DavidDavid (25)

在這個例子中,我們根據 AgeName 屬性創建了一個復合鍵,并對 people 列表進行了分組。

示例 5:多次查詢

由于 ILookup<TKey, TElement> 是預先計算好的,因此可以多次查詢而不會重復執行分組操作。例如:

var lookupPeople = people.ToLookup(p => p.Age);// 第一次查詢
foreach (var group in lookupPeople)
{Console.WriteLine($"Age: {group.Key}");foreach (var person in group){Console.WriteLine($"  {person}");}
}// 第二次查詢
if (lookupPeople.Contains(30))
{Console.WriteLine("Age 30 exists:");foreach (var person in lookupPeople[30]){Console.WriteLine($"  {person}");}
}

在這個例子中,我們展示了如何多次查詢同一個 ILookup<TKey, TElement> 對象而不重復執行分組操作。

4. ILookup<TKey, TElement> 接口

ILookup<TKey, TElement> 接口提供了以下主要成員:

  • Item[TKey]:通過鍵訪問對應的元素集合。
  • Count:獲取組的數量。
  • Contains(TKey):檢查是否存在具有指定鍵的組。

示例 1:使用 ILookup 的特性

class Person
{public string Name { get; set; }public int Age { get; set; }public override string ToString(){return $"{Name} ({Age})";}
}class Program
{static void Main(){List<Person> people = new List<Person>{new Person { Name = "Alice", Age = 30 },new Person { Name = "Bob", Age = 25 },new Person { Name = "Charlie", Age = 30 },new Person { Name = "David", Age = 25 }};// 使用 ToLookup 根據 Age 屬性進行分組var lookupPeople = people.ToLookup(p => p.Age);// 通過鍵訪問對應的元素集合if (lookupPeople.Contains(30)){Console.WriteLine("Age 30 exists:");foreach (var person in lookupPeople[30]){Console.WriteLine($"  {person}");}}// 獲取組的數量Console.WriteLine($"Total groups: {lookupPeople.Count}");}
}

輸出結果:

Age 30 exists:Alice (30)Charlie (30)
Total groups: 2

在這個例子中,我們展示了如何使用 ILookup 的特性,如通過鍵訪問對應的元素集合、檢查是否存在具有指定鍵的組以及獲取組的數量。

4. 注意事項

  • ToLookup 方法是一個立即執行的操作,這意味著它會立即對源集合進行分組操作,并將結果存儲在 Lookup 對象中。
  • 分組鍵必須是可比較的,否則會引發異常。
  • 如果源集合為空,ToLookup 方法將返回一個空的 Lookup 對象。

四、 ToLookup 和 GroupBy 的區別

Enumerable.ToLookup 是 LINQ(Language Integrated Query)中的一個方法,用于將數據源轉換為 ILookup<TKey, TElement> 類型的對象。與 GroupBy 方法類似,ToLookup 也用于對集合進行分組,但有一些關鍵的區別:

1. 概覽

1)Enumerable.ToLookup

  • 即時求值ToLookup 方法會立即遍歷整個源集合并構建結果。
  • 不可變性:返回的 ILookup<TKey, TElement> 對象是只讀的,一旦創建就不能修改。
  • 多次查詢:由于 ILookup<TKey, TElement> 是預先計算好的,因此可以多次查詢而不會重復執行分組操作。
  • 返回類型:返回一個 ILookup<TKey, TElement> 集合。

2)Enumerable.GroupBy

  • 惰性求值GroupBy 方法采用惰性求值,只有在實際遍歷時才會執行分組操作。
  • 可變性:返回的 IEnumerable<IGrouping<TKey, TElement>> 對象是可變的,可以根據需要動態添加或移除元素(盡管通常不建議這樣做)。
  • 單次查詢:每次遍歷時都會重新執行分組操作,適合處理流式數據或無限序列。
  • 返回類型:返回一個 IEnumerable<IGrouping<TKey, TElement>> 集合。

2. 主要區別

1. 求值方式

  • ToLookup:立即求值。調用 ToLookup 方法時,它會立即遍歷整個源集合并構建結果。這意味著所有分組操作在調用時完成,并且結果存儲在內存中。這可能會消耗更多的內存,特別是在處理大型數據集時。

    var lookup = source.ToLookup(x => x.Key);
    // 立即執行分組操作
    
  • GroupBy:惰性求值。調用 GroupBy 方法時,它并不會立即執行分組操作,而是等到實際遍歷時才進行計算。這使得 GroupBy 可以處理無限序列或延遲執行復雜的查詢。這可以節省內存,特別是在處理大型數據集時。

    var groupBy = source.GroupBy(x => x.Key);
    // 分組操作直到實際遍歷時才會執行
    

2. 結果類型與特性

  • ToLookup:返回一個 ILookup<TKey, TElement> 對象,該對象是只讀的,并且支持通過鍵快速訪問對應的元素集合。

    var lookup = source.ToLookup(x => x.Key);
    foreach (var group in lookup)
    {Console.WriteLine($"Key: {group.Key}");foreach (var item in group){Console.WriteLine($"  {item}");}
    }// 通過鍵訪問特定組
    if (lookup.Contains(someKey))
    {foreach (var item in lookup[someKey]){Console.WriteLine(item);}
    }
    
  • GroupBy:返回一個 IEnumerable<IGrouping<TKey, TElement>> 集合,每個 IGrouping<TKey, TElement> 對象包含一個鍵和屬于該鍵的所有元素。由于是惰性求值,每次遍歷時都會重新執行分組操作。

    var groupBy = source.GroupBy(x => x.Key);
    foreach (var group in groupBy)
    {Console.WriteLine($"Key: {group.Key}");foreach (var item in group){Console.WriteLine($"  {item}");}
    }
    

3. 多次查詢

  • ToLookup:由于 ILookup<TKey, TElement> 是預先計算好的,因此可以多次查詢而不會重復執行分組操作。這對于需要頻繁訪問分組結果的應用場景非常有用。

    var lookup = source.ToLookup(x => x.Key);
    // 第一次查詢
    foreach (var group in lookup) { ... }// 第二次查詢
    if (lookup.Contains(someKey)) { ... }
    
  • GroupBy:每次遍歷時都會重新執行分組操作。如果需要多次查詢同一個分組結果,可能會導致性能問題。

    var groupBy = source.GroupBy(x => x.Key);
    // 每次遍歷時都會重新執行分組操作
    foreach (var group in groupBy) { ... }
    

4. 自定義比較器

  • ToLookup:可以通過傳遞 IEqualityComparer<TKey> 實現來使用自定義比較器。

    var customLookup = source.ToLookup(x => x.Key, new CustomComparer());
    
  • GroupBy:也可以通過傳遞 IEqualityComparer<TKey> 實現來使用自定義比較器。

    var customGroupBy = source.GroupBy(x => x.Key, new CustomComparer());
    

5. 投影分組結果

  • ToLookup:可以使用 elementSelector 參數來自定義分組后的結果。

    var lookup = source.ToLookup(x => x.Key, x => x.Value);
    
  • GroupBy:同樣可以使用 elementSelector / resultSelector參數來自定義分組后的結果。

    var groupBy = source.GroupBy(x => x.Key, x => x.Value);
    

3. 適用場景

1. ToLookup 的適用場景

  • 頻繁查詢:當你需要頻繁查詢分組結果,并希望避免重復執行分組操作時,ToLookup 是更好的選擇。

    var lookup = people.ToLookup(p => p.Age);
    // 可以多次查詢不同的年齡組
    
  • 固定數據集:當你的數據集是固定的,不需要動態添加或移除元素時,ToLookup 更加合適。

    var lookup = fixedData.ToLookup(x => x.Key);
    
  • 高性能需求:由于 ToLookup 是立即求值的,對于需要高性能的應用場景,它可以提供更快的查詢速度。

2. GroupBy 的適用場景

  • 惰性求值:當你需要處理流式數據或無限序列時,GroupBy 的惰性求值特性非常適合這種場景。

    var infiniteNumbers = Enumerable.Range(0, int.MaxValue).Select(i => i * 2);
    var groupedNumbers = infiniteNumbers.GroupBy(n => n % 3);
    
  • 動態數據集:如果你的數據集是動態變化的,并且你可能需要根據新的數據動態調整分組結果,GroupBy 更加靈活。

    var dynamicData = GetDataStream();
    var groupBy = dynamicData.GroupBy(x => x.Key);
    
  • 一次性查詢:如果你只需要對數據進行一次分組查詢,那么 GroupBy 可能更簡單且高效。

Enumerable.ToLookupGrouping.GroupBy 都是 LINQ 中用于對集合進行分組的方法,但它們在返回類型和使用場景上有一些重要的區別。


結語

回到目錄頁:C#/.NET 知識匯總
希望以上內容可以幫助到大家,如文中有不對之處,還請批評指正。


參考資料:
微軟官方文檔 Enumerable

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/pingmian/71991.shtml
繁體地址,請注明出處:http://hk.pswp.cn/pingmian/71991.shtml
英文地址,請注明出處:http://en.pswp.cn/pingmian/71991.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

推理模型對SQL理解能力的評測:DeepSeek r1、GPT-4o、Kimi k1.5和Claude 3.7 Sonnet

引言 隨著大型語言模型&#xff08;LLMs&#xff09;在技術領域的應用日益廣泛&#xff0c;評估這些模型在特定技術任務上的能力變得越來越重要。本研究聚焦于四款領先的推理模型——DeepSeek r1、GPT-4o、Kimi k1.5和Claude 3.7 Sonnet在SQL理解與分析方面的能力&#xff0c;…

IDEA接入阿里云百煉中免費的通義千問[2025版]

安裝deepseek 上一篇文章IDEA安裝deepseek最新教程2025中說明了怎么用idea安裝codeGPT插件&#xff0c;并接入DeepSeek&#xff0c;無奈接入的官方api已經不能使用了&#xff0c;所以我們嘗試從其他地方接入 阿里云百煉https://bailian.console.aliyun.com/ 阿里云百煉?是阿…

實施一套先進的智能攝像頭服務系統。

一、項目背景 隨著物聯網、人工智能和大數據技術的飛速發展&#xff0c;智能攝像頭已成為家庭、企業以及公共安全領域的重要設備。其便捷、高效、智能的特點&#xff0c;使得市場需求日益增長。為了滿足用戶對智能監控的多樣化需求&#xff0c;提供更加全面、可靠的監控服務&a…

linux自啟動服務

在Linux環境中&#xff0c;systemd是一個系統和服務管理器&#xff0c;它為每個服務使用.service文件進行配置。systemctl是用于控制系統服務的主要工具。本文將詳細介紹如何使用systemctl來管理vsftpd服務&#xff0c;以及如何設置服務自啟動。 使用Systemd設置自啟動服務 創…

010-Catch2

Catch2 一、框架簡介 Catch2 是一個基于 C 的現代化單元測試框架&#xff0c;支持 TDD&#xff08;測試驅動開發&#xff09;和 BDD&#xff08;行為驅動開發&#xff09;模式。其核心優勢在于&#xff1a; 單頭文件設計&#xff1a;v2.x 版本僅需包含 catch.hpp 即可使用自然…

數字人分身開發指南:從概念到實戰

一、什么是數字人分身&#xff1f; 想象一下&#xff0c;在電腦或手機屏幕里&#xff0c;一個能跟你聊天、回答問題&#xff0c;甚至還能做表情的虛擬角色。這就是數字人分身&#xff0c;它用上了人工智能技術&#xff0c;讓機器也能像人一樣交流。無論是在線客服、網絡主播還…

Pixelmator Pro for Mac 專業圖像處理軟件【媲美PS的修圖】

介紹 Pixelmator Pro&#xff0c;是一款非常強大、美觀且易于使用的圖像編輯器&#xff0c;專為 Mac 設計。采用單窗口界面、基于機器學習的智能圖像編輯、自動水平檢測&#xff0c;智能快速選擇及更好的修復工具等功能優點。許多非破壞性的專業編輯工具可讓您進行最佳的照片處…

LiveGBS流媒體平臺GB/T28181常見問題-視頻流安全控制HTTP接口鑒權勾選流地址鑒權后401Unauthorized如何播放調用接口流地址校驗

LiveGBS流媒體平臺GB/T28181常見問題頻流安全控制HTTP接口鑒權勾選流地址鑒權后401Unauthorized如何播放調用接口流地址校驗&#xff1f; 1、安全控制1.1、HTTP接口鑒權1.2、流地址鑒權 2、401 Unauthorized2.1、攜帶token調用接口2.1.1、獲取鑒權token2.1.2、調用其它接口2.1.…

C++設計模式-抽象工廠模式:從原理、適用場景、使用方法,常見問題和解決方案深度解析

一、模式基本概念 1.1 定義與核心思想 抽象工廠模式&#xff08;Abstract Factory Pattern&#xff09;是創建型設計模式的集大成者&#xff0c;它通過提供統一的接口來創建多個相互關聯或依賴的對象族&#xff0c;而無需指定具體類。其核心思想體現在兩個維度&#xff1a; …

【prompt實戰】知乎問題解答專家

本文原創作者&#xff1a;姚瑞南 AI-agent 大模型運營專家&#xff0c;先后任職于美團、獵聘等中大廠AI訓練專家和智能運營專家崗&#xff1b;多年人工智能行業智能產品運營及大模型落地經驗&#xff0c;擁有AI外呼方向國家專利與PMP項目管理證書。&#xff08;轉載需經授權&am…

數據結構第八節:紅黑樹(初階)

【本節要點】 紅黑樹概念紅黑樹性質紅黑樹結點定義紅黑樹結構紅黑樹插入操作的分析 一、紅黑樹的概念與性質 1.1 紅黑樹的概念 紅黑樹 &#xff0c;是一種 二叉搜索樹 &#xff0c;但 在每個結點上增加一個存儲位表示結點的顏色&#xff0c;可以是 Red和 Black 。 通過對 任何…

Spring Boot3.3.X整合Mybatis-Plus

前提說明&#xff1a; 項目的springboot版本為&#xff1a;3.3.2 需要整合的mybatis-plus版本&#xff1a;3.5.7 廢話不多說&#xff0c;開始造吧 1.準備好數據庫和表 2.配置全局文件application.properties或者是application.yml&#xff08;配置mapper的映射文件路徑&am…

可視化圖解算法:鏈表指定區間反轉

1. 題目 描述 給你單鏈表的頭指針 head 和兩個整數 left 和 right &#xff0c;其中 left < right 。請你反轉從位置 left 到位置 right 的鏈表節點&#xff0c;返回 反轉后的鏈表 。 示例1 輸入&#xff1a; 輸入&#xff1a;head [1,2,3,4,5], left 2, right 4 輸…

?SQL-遞歸CTE

&#x1f4d6; SQL魔法課堂&#xff1a;CTE「時間折疊術」全解 &#x1f3a9; 第一章&#xff1a;什么是CTE&#xff1f; CTE&#xff08;Common Table Expression&#xff09; 就像 SQL 里的「臨時筆記本」&#x1f4d2;&#xff1a; WITH 臨時筆記本 AS ( SELECT ... FRO…

Cursor 新手入門使用教程

一、Cursor 是什么&#xff1f; Cursor 是一個集成了 GPT-4、Claude 3.5 等先進 LLM&#xff08;大語言模型&#xff09;的類 VSCode 編譯器&#xff0c;可以理解為在 VSCode 中集成了 AI 輔助編程助手。從界面布局來看&#xff0c;Cursor 與 VSCode 基本一致&#xff0c;且使…

如何在Spring Boot中配置和使用MyBatis-Plus

在當今的Java開發中&#xff0c;Spring Boot已經成為了一個非常流行的框架&#xff0c;而MyBatis-Plus則是一個強大的ORM框架&#xff0c;為開發人員提供了更簡便的數據庫操作方式。很多開發者都在使用Spring Boot和MyBatis-Plus的組合來快速構建高效的應用。今天就來聊聊如何在…

【貪心算法3】

力扣1005.k次取反后最大化的數組和 鏈接: link 思路 既然要求最大和&#xff0c;那么不妨先給數組排個序&#xff0c;如果有負數&#xff0c;先處理負數從前往后給數組取反&#xff0c;如果負數處理完后k還有次數&#xff0c;此時數組全是正數了&#xff0c;只需要對第一個元…

自然語言處理中的語音識別技術:從聲波到語義的智能解碼

引言 語音識別&#xff08;Automatic Speech Recognition, ASR&#xff09;是自然語言處理&#xff08;NLP&#xff09;的關鍵分支&#xff0c;旨在將人類語音信號轉化為可處理的文本信息。隨著深度學習技術的突破&#xff0c;語音識別已從實驗室走向日常生活&#xff0c;賦能…

1688店鋪所有商品數據接口詳解

??一、接口概述淘寶開放平臺提供 1688.items.onsale.get/taobao.item_search_shop 接口&#xff0c;可批量獲取店鋪在售商品列表&#xff0c;包含商品 ID、標題、價格、銷量、圖片等核心信息。該接口適用于商品庫管理、競品監控、數據分析等場景 ?二、接口調用流程 前期準…

ArduPilot開源代碼之AP_OSD

ArduPilot開源代碼之AP_OSD 1. 源由2. 簡介3. 補丁4. 框架設計4.1 啟動代碼 (AP_OSD::init)4.2 任務代碼 (AP_OSD::osd_thread)4.3 實例初始化 (AP_OSD::init_backend) 5. 重要例程5.1 AP_OSD::update_stats5.2 AP_OSD::update_current_screen5.3 AP_OSD::update_osd 6. 總結7.…