[C#] 28. リスト(List)とディクショナリ(Dictionary)、そしてLinq式を使い方


Study / C#    作成日付 : 2019/07/16 22:40:03   修正日付 : 2021/09/13 19:14:55

こんにちは。明月です。


この投稿はC#のリスト(List)とディクショナリ(Dictionary)、そしてLinq式を使い方に関する説明です。


以前の投稿で配列とオブジェクト指向プログラミング(OOP)に関して説明したことがあります。에 대해서 설명한 적이 있습니다.

link - [C#] 8. 配列とリスト

link - [C#] 20. オブジェクト指向プログラミング(OOP)の4つの原則(カプセル化、抽象化、継承化、多相化(ポリモーフィズム))


オブジェクト指向プログラミング(OOP)に関して簡単に改めて説明するとプログラムを開発する時、すべてをオブジェクト(Object)に考えて開発することという意味です。

つまり、オブジェクトというのはプログラムでクラス(Class)の形で管理すること、またはこのオブジェクトを効果的に管理するためにリスト(List)とディクショナリ(Dictionary)をよく使います。


リストはデータ構造アルゴリズムでは連結リストアルゴリズムだし、ディクショナリ(Dictionary)はマップアルゴリズムです。

連結リストのアルゴリズムは始めから最後までのデータをポインタで連結したことだし、ディクショナリはキーと値で連結したデータ構造です。

using System;
using System.Collections.Generic;

namespace Example
{
  // 例クラス
  class Node
  {
    // 値をコンストラクタで格納する。
    public Node(int data)
    {
      // プロパティのDataに値を格納
      this.Data = data;
    }
    // Dataプロパティ
    public int Data
    {
      // 入力はコンストラクタから受け取る。
      get; private set;
    }
  }
  class Program
  {
    // 実行関数
    static void Main(string[] args)
    {
      // リスト宣言(リストのオブジェクトはNodeクラス)
      var list = new List<Node>();
      // iが0から9まで繰り返し
      for (int i = 0; i < 10; i++)
      {
        // リストにデータを挿入
        list.Add(new Node(i));
      }
      // リストの5番目のNodeインスタンスを取得
      var removeNode = list[5];
      // リストから取り除く。
      list.Remove(removeNode);
      
      // リストの2番目にDataが100のNodeインスタンスを挿入
      list.Insert(2, new Node(100));
      
      // listの値を順番とおりに出力
      foreach (var node in list)
      {
        // コンソール出力
        Console.WriteLine(node.Data);
      }
      // 任意のキーを押してください
      Console.WriteLine("Press any key...");
      Console.ReadLine();
    }
  }
}


リストから0の値から9までのDataを持っているNodeインスタンスを順番とおりに格納されています。

リストは確実に順番が決めているし、5番目のNodeインスタンスを取得してリストから取り除きました。

そして、2番目のリストに100のDataを持っているNodeインスタンスを挿入しました。


これかリストの特性です。


ディクショナリ(Dictionary)の場合は始めから最後までの順番が決めていることではなく、Keyからデータを取得します。

using System;
using System.Collections.Generic;

namespace Example
{
  // 例クラス
  class Node
  {
    // 値をコンストラクタで格納する。
    public Node(int data)
    {
      // プロパティのDataに値を格納
      this.Data = data;
    }
    // Dataプロパティ
    public int Data
    {
      // 入力はコンストラクタから受け取る。
      get; private set;
    }
  }
  class Program
  {
    // 実行関数
    static void Main(string[] args)
    {
      // ディクショナリ宣言(ディクショナリのキーはString、オブジェクトはNodeクラス)
      var dic = new Dictionary<string, Node>();
      // iが0から9まで繰り返し。
      for (int i = 0; i < 10; i++)
      {
        // ディクショナリのキーは"Key" + iの値でデータを挿入。
        dic.Add("Key" + i, new Node(i));
      }
      // ディクショナリのキーがKey2の値を取り除く。
      dic.Remove("Key2");
      // ディクショナリのキーがkey100のデータを挿入。
      dic.Add("key100", new Node(100));
      // ディクショナリのKeyを取得して個数ほど繰り返し。
      foreach (var key in dic.Keys)
      {
        // ディクショナリのKeyの値でデータを取得
        var node = dic[key];
        // コンソール出力
        Console.WriteLine("Key = " + key + ", value = " + node.Data);
      }
      // 任意のキーを押してください
      Console.WriteLine("Press any key...");
      Console.ReadLine();
    }
  }
}


ディクショナリのKeyを通ってデータを入力して取り除く、出力しました。

リストと違うのはデータが順番とおりにあることではなく、keyというデータを通ってデータが管理することを確認できます。

参考でKeyの値はリストの形式で順番とおりに格納されていることではなく、Keysでキーのリスト(?)を取得するとリストが順番とおりではないことを確認できます。


C#はオブジェクト指向プログラム言語(OOP)としてデータを管理するためにリストとディクショナリをたくさん使います。

リストで上の例みたいに入力、出力することができますが、特定データを検索するために我々は繰り返しを使います。

using System;
using System.Collections.Generic;

namespace Example
{
  // 例クラス
  class Node
  {
    // 値をコンストラクタで格納する。
    public Node(int data)
    {
      // プロパティのDataに値を格納
      this.Data = data;
    }
    // Dataプロパティ
    public int Data
    {
      // 入力はコンストラクタから受け取る。
      get; private set;
    }
  }
  class Program
  {
    // 実行関数
    static void Main(string[] args)
    {
      // リスト宣言(リストのオブジェクトはNodeクラス)
      var list = new List<Node>();
      // iが0から9まで繰り返し
      for (int i = 0; i < 10; i++)
      {
        // リストにデータを挿入
        list.Add(new Node(i));
      }
      // 偶数の値だけ他のリストで格納する。
      var evenList = new List<Node>();
      // listの値を順番とおりに出力
      foreach (var node in list)
      {
        // nodeインスタンスのDataが偶数の場合
        if (node.Data % 2 == 0)
        {
          // リストに格納
          evenList.Add(node);
        }
      }
      // evenListの値を順番とおりに出力
      foreach (var node in evenList)
      {
        // コンソール出力
        Console.WriteLine(node.Data);
      }

      // 任意のキーを押してください
      Console.WriteLine("Press any key...");
      Console.ReadLine();
    }
  }
}


listのデータの値が偶数のデータを検索してevenListのリストにインスタンスを格納しました。

そしてevenListを繰り返しで出力すると結果は0が含めている偶数だけ出力することを確認できます。


上の例は間違いソースではありませんが、C#ではもっと効率的にフィルター、抽出する方法があります。

using System;
using System.Collections.Generic;
using System.Linq;

namespace Example
{
  // 例クラス
  class Node
  {
    // 値をコンストラクタで格納する。
    public Node(int data)
    {
      // プロパティのDataに値を格納
      this.Data = data;
    }
    // Dataプロパティ
    public int Data
    {
      // 入力はコンストラクタから受け取る。
      get; private set;
    }
  }
  class Program
  {
    // 実行関数
    static void Main(string[] args)
    {
      // リスト宣言(リストのオブジェクトはNodeクラス)
      var list = new List<Node>();
      // iが0から9まで繰り返し
      for (int i = 0; i < 10; i++)
      {
        // リストにデータを挿入
        list.Add(new Node(i));
      }

      // 偶数の値だけ他のリストに格納する。
      var evenList = from node in list where node.Data % 2 == 0 select node;
      // evenListの値を順番とおりに出力
      foreach (var node in evenList)
      {
        // コンソール出力
        Console.WriteLine(node.Data);
      }

      // 任意のキーを押してください
      Console.WriteLine("Press any key...");
      Console.ReadLine();
    }
  }
}


上の例はデータベースのSQLクエリみたいなプログラム式があります。

from node in list는 listの意味は各アイテムの値をnodeに置換して、whereの条件式でnode.Dataが偶数でフィルターしてselectで出力して新しいリストを生成しました。

そしてforeachで出力すると結果は上の例と同じ値を出力しました。


上みたいなプログラム式をC#ではLinqクエリ式だといいます。

リンククエリ式は上みたいにフィルター(where)の役割もありますが、整列と集合の式もあります。

using System;
using System.Collections.Generic;
using System.Linq;

namespace Example
{
  // 例クラス
  class Node
  {
    // 値をコンストラクタで格納する。
    public Node(int data)
    {
      // プロパティのDataに値を格納
      this.Data = data;
    }
    // Dataプロパティ
    public int Data
    {
      // 入力はコンストラクタから受け取る。
      get; private set;
    }
  }
  class Program
  {
    // 実行関数
    static void Main(string[] args)
    {
      // リスト宣言(リストのオブジェクトはNodeクラス)
      var list = new List<Node>();
      // iが0から9まで繰り返し
      for (int i = 0; i < 10; i++)
      {
        // リストにデータを挿入
        list.Add(new Node(i));
      }

      // 偶数、奇数別でグループを分けてそのキーでNodeを再整列する。
      var filerList = from node in list orderby node.Data descending group node by node.Data % 2 into g select (key: g.Key, value: g);
      // filerListのキーの順番とおりに繰り返し
      foreach (var item in filerList)
      {
        // 各キーの中でリストを出力する。
        foreach (var value in item.value)
        {
          // コンソール出力
          Console.WriteLine("Key : " + item.key + " Value : " + value.Data);
        }
        // 改行
        Console.WriteLine();
      }

      // 任意のキーを押してください
      Console.WriteLine("Press any key...");
      Console.ReadLine();
    }
  }
}


上の例ではorderbyでリストを降順(descending)で再整列してgroup byで1と0で再整列してselectでディクショナリの形でデータを生成します。

簡単にDictionary<int, List<Node>>の形で再構成することです。


プログラム設計によりデータベースクエリ式みたいに作られますが、他の方式でプログラム関数式で作成することもできます。

using System;
using System.Collections.Generic;
using System.Linq;

namespace Example
{
  // 例クラス
  class Node
  {
    // 値をコンストラクタで格納する。
    public Node(int data)
    {
      // プロパティのDataに値を格納
      this.Data = data;
    }
    // Dataプロパティ
    public int Data
    {
      // 入力はコンストラクタから受け取る。
      get; private set;
    }
  }
  class Program
  {
    // 実行関数
    static void Main(string[] args)
    {
      // リスト宣言(リストのオブジェクトはNodeクラス)
      var list = new List<Node>();
      // iが0から9まで繰り返し
      for (int i = 0; i < 10; i++)
      {
        // リストにデータを挿入
        list.Add(new Node(i));
      }
      // 偶数の値だけに他のリストで格納する。
      var evenList = list.Where(x => x.Data % 2 == 0);
      // evenListの値を順番とおりに出力
      foreach (var node in evenList)
      {
        // コンソール出力
        Console.WriteLine(node.Data);
      }
      // 改行
      Console.WriteLine();

      // 偶数、奇数別にグループを分けて、そのキーでNodeを再整列する。
      var filerList = list.OrderByDescending(x => x.Data).GroupBy(x => x.Data % 2).Select(x => (key: x.Key, value: x));
      // filerListのキー順番で繰り返し
      foreach (var item in filerList)
      {
        // 各キーの中でリストを出力する。
        foreach (var value in item.value)
        {
          // コンソール出力
          Console.WriteLine("Key : " + item.key + " Value : " + value.Data);
        }
        // 改行
        Console.WriteLine();
      }

      // 任意のキーを押してください
      Console.WriteLine("Press any key...");
      Console.ReadLine();
    }
  }
}


プログラムの関数みたいにリストのフィルダーを作られます。

結果は上のクエリ式と同じ結果が出力します。


C#にはオブジェクトをリスト(List)とディクショナリ(Dictionary)でオブジェクト(Object)を管理してLinqのクエリ式と関数式を通ってデータをフィルター、分類して使います。

Linq式には代表的に一番よく使うのはwhereとselectですが、仕様により二つのリストを合体(Join)、重複データの取り除く(Distinct)、合集合(Union)などの機能があります。

少し、もっと詳細のは他の投稿で説明します。


ここまでC#のリスト(List)とディクショナリ(Dictionary)、そしてLinq式を使い方に関する説明でした。


ご不明なところや間違いところがあればコメントしてください。

#C#
最新投稿