見出し画像

LINQとCollectionは違うらしい【働学併進#005】

C#を勉強していた当初、LINQとは配列 Array やリスト List といった複数のデータを効率よく扱うための型だと勘違いしていた。
しかし本当のところは「Collectionは複数の要素を保持できる型」のことであり、「LINQはC#に組み込まれたクエリー技術 Language Integrated Query」だったのだ。

因みに、Queryとは「データの更新・削除・抽出・選択などの要求」のことである。例えば、noteの検索機能や自分の記事の削除もQueryである。

なぜ、わざわざC#の言語レベルで新たにクエリーを扱えるようにしたか。
それは「Collection型やSQL, XMLなどに対するクエリーの書き方を統合するため」だ。
つまり、「LINQとはCollection型やデータベース(SQL)などへの要求を同じように扱うためのC#の技術」とも言える。

冒頭だけでC#初学者(昔の私)の疑問の答えになっているだろうが、以降はCollectionとLINQについて、もう少し詳しくまとめる。

Collectionとは?

C#のCollection型は「複数の要素を保持できる型」と先ほど説明したが、実はCollectionには以下の3つの種類がある。

  1. System.Collections のCollections

  2. System.Collections.Generic のCollections

  3. System.Collections.Concurrent のCollections

順にまとめる。

System.Collections のCollections

System.Collectionsのnamespace から呼び出せる以下のような型を用いることで、どのような型でもObject型として保持するコレクションを作ることができる。

  • ArrayList: サイズが動的に変わるコレクション

  • Hashtable: キーと値のペアのコレクション

  • Queue: First In, First Out (FIFO)のコレクション

  • Stack: Last In, First Out (LIFO)のコレクション

複数の型の項目 item を保持できるということは、コレクションから要素を取得するだけでもデータ型を判断、ときには変換する必要がある。だから、基本的には以下で紹介するCollections.GenericかCollections.Concurrentの名前空間 namespace のコレクションを使うことをMicrosoft も推奨してる。

Collections.Generic のCollections

System.Collections.Genericのnamespace から呼び出せる以下のような型を用いることでジェネリックコレクション(要素が全て同じ型のコレクション)を作成できる。

  • List<T>:
    インデックスで項目 item を管理するコレクション
    検索、並べ替え、変更のメソッドが用意されている

  • Dictionary<TKey, TValue>: キーと値のペアのコレクション

  • Queue<T>: First In, First Out (FIFO) のコレクション

  • Stack<T>: Last In, First Out (LIFO) のコレクション

「ジェネリック」という言葉から「さまざまな型を同時に保持できる」型だと勘違いするかもしれないが、正しくは「コレクションを宣言する際に指定された単一の方しか保持できないが、どのような型でも指示できる」という意味だ。

Collections.ConcurrentのCollections

System.Collections.Concurrent のnamespace から呼び出せる以下のような型を用いることで、スレッドセーフ thread-safe に項目 item にアクセスできるコレクションを作成できる。

  • BlockingCollection<T>: ブロッキングと範囲指定ができるコレクション

  • ConcurrentBag<T>: 順序づけられていない unordered なコレクション

  • ConcurrentDictionary<TKey, TValue>

  • ConcurrentQueue<T>: FIFO のコレクション

  • ConcurrentStack<T>: LIFO のコレクション

List vs. Array

どちらも「指定された1つの型の要素を複数保持できる」点や、foreachステートメントが使える(System.Collections.IEnumerableかSystem.Collections.Generic.IEnumerable<T>インターフェースを実装している)という点では同じだ。

しかし、Listはその大きさを動的に変えることができる一方、Arrayは宣言する際に指定した大きさのままである。
この違いを表すために、Arrayの要素数を取得するときは予め決められた長さという意味で .Length プロパティを使い、List などは動的に変化する要素数を数えるという意味で.Count プロパティを使う。

LINQとは?

前述の通り、LINQとは言語に組み込まれた、Collection やデータベースなどへのクエリーである。
因みに、LINQは「SQLのようにフィルター、並べ替え、グループなどを行うクエリー」と「Collection の各要素を扱うメソッド」という二つの意味で使われることがあり、前者をLINQクエリ、後者をLINQのメソッドなどと言われる(私的には好きでない)。

Filtering, Ordering, and Grouping

private static void ShowLINQ()
{
    List<Element> elements = new List<Element>
    {
        { new Element() { Symbol="K", Name="Potassium", AtomicNumber=19}},
        { new Element() { Symbol="Ca", Name="Calcium", AtomicNumber=20}},
        { new Element() { Symbol="Sc", Name="Scandium", AtomicNumber=21}},
        { new Element() { Symbol="Ti", Name="Titanium", AtomicNumber=22}}
    };
    var subset = from theElement in elements
                 where theElement.AtomicNumber < 22
                 orderby theElement.Name
                 select theElement;

    foreach (Element theElement in subset)
    {
        Console.WriteLine(theElement.Name + " " + theElement.AtomicNumber);
    }

    // Output:
    //  Calcium 20
    //  Potassium 19
    //  Scandium 21
}

元素番号が22より小さく(where theElement.Atomic Number < 22)、アルファベット順に(orderby theElement.Name)、Element型のtheElementを要素とした(select theElement)コレクションを返す。

このように、from in, where, orderby, select などを使ったクエリ(クエリ式 query expression)に対して、以下で説明するようにメソッドのようにクエリを書くこともできる。

Standard Query Operator Extension Methods
標準クエリ演算子の拡張メソッド

LINQ標準クエリ演算子(LINQの拡張メソッド)とは、既存のSystem.Collections.IEnumerable かSystem.Collections.Generic.IEnumerable<T>型に追加された拡張メソッド。
クエリ式を、コレクションからメソッドを呼び出すように記述することができる。

因みに、ArrayもIEnmerableを実装しているためLINQ標準クエリ演算子が使える。

using System.Linq;

class ExtensionMethods2
{
    static void Main()
    {
        int[] ints = { 10, 45, 15, 39, 21, 26 };
        var result = ints.OrderBy(g => g);
        foreach (var i in result)
        {
            System.Console.Write(i + " ");
        }
    }
}
// => 10 15 21 26 26 39 45

以下のことを忘れるべからず。

  • using System.Linq ディレクティブで、標準クエリ演算子をスコープに含める

  • 標準クエリ演算子は元のデータを編集するものではないため、結果は別な変数で受け取る。

以下が標準クエリ演算子の一覧だが、詳しいことは公式ドキュメントを見てほしい。

  • 並べ替え sort

    • OrderBy, OrderByDescending

    • ThenBy, ThenByDescending

    • Reverse

  • セット操作 set

    • Distinct[By]

    • Except[By]

    • Intersect[By]

    • Union[By]

  • フィルター処理 filter

    • Where, OfType

  • 量指定子操作 quantifier

    • All

    • Any

    • Contains

  • 射影操作

    • Select, SelectMany

    • Zip

  • パーティション分割 partition

    • Skip, SkipWhile, Take, TakeWhile

  • 結合演算 join

    • Join, GroupJoin

  • グループ化 group

    • GroupBy, ToLookup

  • 生成操作 generate

    • DefaultEmpty, Empty

    • Range

    • Repeat

  • 等値演算 equality

    • SequenceEqual

  • 要素操作 element operation

    • ElementAt, ElementAtOrDefault

    • First, FirstOrDefault

    • Last, LastOrDefault

    • Single, SingleOrDefault

  • データ型の変換 convert

    • AsEnumerable, AsQueryable, Cast, OfType

    • ToArray, ToDictionary, ToList, ToLookup

  • 連結演算 concatenate

    • Concat

  • 集計操作 aggregate

    • Aggregate

    • Average

    • Count, LongCount,

    • Max[By], Min[By]

    • Sum


#001から#005(月曜から金曜)までよく頑張った。
明日はゆっくり読書でもして、読書ノートを公開しよう。

この記事が気に入ったらサポートをしてみませんか?