いろいろ備忘録日記

主に .NET とか Go とか Flutter とか Python絡みのメモを公開しています。

.NET クラスライブラリ探訪-011 (System.Collections.Generic.Dictionary)(01)


Dictionaryクラスは、他の言語にあるハッシュと同じようなものです。
キーと値のペアでデータを登録し、利用します。


このクラスは、ジェネリック対応となっており、オブジェクト生成時に
型引数を渡す必要があります。


また、このクラスはIDictionaryインターフェースを実装しています。


今回は、基本的なメソッドおよびプロパティを確認してみて、
次で、Comparerプロパティを使用して値の同値性のテストをしてみたいと思います。


以下、サンプルです。

using System;
using System.Collections.Generic;
using System.Text;
using System.Threading;
using System.Collections;

namespace Gsf.Samples.ClassLibraryResearch.System.Collections.Generic {

    /// <summary>
    /// Dictionaryクラスについてのサンプルです。
    /// </summary>
    /// <remarks>
    /// Dictionaryクラスは、他の言語にあるハッシュと同じようなものです。
    /// キーと値のペアでデータを登録し、利用します。
    /// 
    /// このクラスは、ジェネリック対応となっており、オブジェクト生成時に
    /// 型引数を渡す必要があります。
    /// 
    /// また、このクラスはIDictionaryインターフェースを実装しています。
    /// </remarks>
    public class DictionaryResearcher : IExecutable {

        #region IExecutable メンバ

        public void Execute() {
            //
            // 空のディクショナリを作成.
            //
            Dictionary<string, string> dict = new Dictionary<string, string>();

            //
            // データの登録
            //
            // 以下の操作は、どれも同じことです。
            //
            dict["value1"] = "gsf_zero1";
            dict.Add("value2", "gsf_zero2");
            (dict as IDictionary<string, string>).Add(new KeyValuePair<string, string>("value3", "gsf_zero3"));
            dict["value4"] = "gsf_zero4";

            //
            // データの表示(loop)
            //
            // IDictionaryは, IEnumerableを継承していますのでforeach可能です。
            // その際、ループ毎にKeyValuePair<T, E>が渡されます。
            //
            foreach(KeyValuePair<string, string> pair in dict) {
                Console.WriteLine("key:{0}, value:{1}", pair.Key, pair.Value);
            }

            //
            // 特定のデータの取得
            //
            // インデクサが実装されているので、添え字にキーを指定すれば取得できます。
            //
            Console.WriteLine("keyがvalue2のデータは{0}", dict["value2"]);

            //
            // 特定のキーが存在しているかどうかを確認
            //
            // Dictionaryは、存在していないキーを指定した場合KeyNotFoundExceptionをスローします。
            // 例外を起こさない為には、事前にContainsKeyなどを使用して調べておくか、以下で記述する
            // TryGetValueメソッドを使用します。
            //
            try {

                //
                // キーが存在しないので例外が発生します。
                //
                string v = dict["not_exists_key"];

            } catch(KeyNotFoundException ex) {
                Console.WriteLine(ex);
            }

            //
            // 事前に確認.
            //
            if(!dict.ContainsKey("not_exists_key")) {
                Console.WriteLine("指定されたキーは存在しません。");
            }

            //
            // 特定の値が存在しているかどうかを確認
            //
            if(!dict.ContainsValue("not_exists_value")) {
                Console.WriteLine("指定された値は存在しません。");
            }

            //
            // このディクショナリに登録されているデータの件数を取得
            //
            Console.WriteLine("データの件数は{0}", dict.Count);

            //
            // キーの存在確認と値取得を一度で行う。
            //
            // TryGetValueメソッドを使用すると、キーが存在している場合戻り値としてtrueが返り、
            // outで渡した変数の中に値が格納されます。
            //
            string value3;
            if(dict.TryGetValue("value3", out value3)) {
                Console.WriteLine("キー:value3は存在しており、値は{0}", value3);
            }

            //
            // キーのコレクションを取得
            //
            // Keysプロパティからは、Dictionary.KeyCollectionオブジェクトが取得できます。
            //
            //
            foreach(string key in dict.Keys) {
                Console.WriteLine(key);

                // この操作はエラーとなる。
                // (コレクションのイテレート中にコレクションのデータを変更しているため)
                //dict[key] = "hehe";
            }

            //
            // 値のコレクションを取得
            //
            // コレクション全体の注意点として、コレクションのイテレート中に該当のコレクションを
            // 変更する操作は行えません。例外が発生します。
            // これは、javaと同じです。
            // 
            // 正常にコレクションのデータを変更する場合は、SyncRootプロパティより同期オブジェクトを
            // 取得し、ロックを行いながら処理を行います。
            //
            foreach(string val in dict.Values) {
                Console.WriteLine(val);
            }

            //
            // 別スレッドで、値を変更しながら他方でイテレートするには以下のように
            // SyncRootを使用しロック制御します。
            //
            // 以下では、スレッドプールを使用してディクショナリのデータを書き換えていますが、
            // スレッドプールのキューに登録して実行されるものであるため、タイミングによっては
            // 先に値のイテレーションの方がロックを獲得する場合があります。その場合は、変更前の
            // 値が表示され、その後プールの処理が走ります。
            //
            // また、SyncRootプロパティはICollectionのメソッドとして明示的に実装されていますので
            // 使用する際は、キャストが必要になります。
            //
            ThreadPool.QueueUserWorkItem(delegate(object o) {
                // この部分でスリープを多めにかけると、先にイテレーションの処理が走ります。
                //Thread.Sleep(500);
                lock((dict as ICollection).SyncRoot) {
                    for(int i = 1; i < dict.Count; i++) {
                        dict["value" + i] = "gsf_zero" + (i + 100);
                    }
                }
            });

            lock((dict as ICollection).SyncRoot) {
                foreach(string val in dict.Values) {
                    Console.WriteLine(val);
                }
            }

            //
            // 登録されているデータの削除
            //
            string tmpValue;
            if(dict.TryGetValue("value1", out tmpValue)){
                Console.WriteLine("キー:value1は存在しており、値は{0}", tmpValue);
            }

            dict.Remove("value1");

            if(!dict.TryGetValue("value1", out tmpValue)){
                Console.WriteLine("キー:value1は存在していません。");
            }

            //
            // 登録されているデータをクリア
            //
            dict.Clear();
            Console.WriteLine("クリア後のデータの件数:{0}", dict.Keys.Count);
        }

        #endregion
    }
}

================================
過去の記事については、以下のページからご参照下さい。

サンプルコードは、以下の場所で公開しています。