読者です 読者をやめる 読者になる 読者になる

謎言語使いの徒然

適当に気になった技術や言語を流すブログ。

.NET を理解する3

.NET C# Tips
.NET のデータ構造

Stack , Heap , Hash どれもちょっとプログラムを齧ったことがあれば聞き覚えはあるでしょう。
もちろん自分で実装する人もいますが、正直それは時間の無駄です。
Java 然り、C++STL 然り、古い言語であっても、そのようなライブラリはすでに実装され、プログラマはそれを使って開発を行います。
.NET もそれは然りで、ここでは Collection と呼ばれるデータ構造のライブラリについて説明します。

System.Collection 名前空間

.NET Framework では、この名前空間の中にコレクションクラスが存在します。
この名前空間は、.NET 1.1 の時代から存在し、もっとも基本的なデータ構造を提供します。

実際のコレクションクラス

ArrayList クラスがもっとも基本かもしれません。これは、IList, ICollection, IEnumerable, ICloneable インターフェースを実装するクラスです。

IList では Add, Insert, Contains, IndexOf, Clear, Remove, RemoveAt メソッドが定義されます。
それぞれ、追加、挿入、含んでいるかの判断、位置の検索、全削除、1件削除、指定位置を削除です。

ICollection では Count, IsSynchronized, SyncRoot プロパティが定義されます。
それぞれ、要素数の取得、スレッドセーフであるか、同期制御オブジェクトの取得プロパティです。

IEnumerable は、列挙型であることを宣言しています。
この実装オブジェクトは、foreach で内部要素を列挙することが可能です。

IEnumerable は IEnumerator GetEnumerator を定義します。
このメソッドで得られた結果を foreach では利用しているわけです。

実際に使っているコード例は以下の通りです。

System.Collections.ArrayList arrayList
    = new System.Collections.ArrayList();

arrayList.Add("HAHAHA");
arrayList.Add("追加");

// object として格納するので、異なる型でも挿入できます。
arrayList.Add(55);

foreach (object data in arrayList)
{
    Console.WriteLine(data);
}

System.Collections.ArrayList seccondList
    = new System.Collections.ArrayList();

seccondList.AddRange(arrayList);
seccondList.Insert(1, "挿入!");

for (int i=0; i < seccondList.Count; i++)
{
    // array 
    Console.WriteLine(seccondList[i]);
}


また、覚えておくと便利な IEnumerable を試しに実装してみたのが以下です。
55 までの等差数列の総和を列挙します。

static void Main(string[] args)
{
    DummyCollection data = new DummyCollection();
    foreach (object val in data)
    {
        Console.WriteLine(val);
    }
}

public class DummyCollection : System.Collections.IEnumerable
{
    public System.Collections.IEnumerator GetEnumerator()
    {
        return new DummyEnumrator();
    }
}

public class DummyEnumrator : System.Collections.IEnumerator
{
    private const int _MaxCount = 55;
    int contentCount;

    public object Current
    {
        get
        {
            Console.WriteLine("DummyEnumrator:Current");
            return Sum(contentCount);
        }
    }

    public bool MoveNext()
    {
        Console.WriteLine("DummyEnumrator:MoveNext");
        this.contentCount++;

        return this.contentCount < _MaxCount;
    }

    public void Reset()
    {
        Console.WriteLine("DummyEnumrator:Reset");
        this.contentCount = 0;
    }

    private int Sum(int i)
    {
        return i == 1 ? i : i + Sum(i - 1);
    }
}

こうして実行すると、foreach の動作が見えるかと思います。

典型的なコレクションとしては、Queue(FIFO), Stack(LIFO) 存在し、Queue では Enqueue, Dequeue があり、Stack には Push, Pop が存在しています。
まぁ基本ですよね?

他にこの名前空間には、Hashtable, DictionaryEntry, SortedList などがあります。
また、ハッシュ等を使う場合、要素に IEqualityComparer を実装し、適切な値を返す事で高速になる場合があります。

簡単にできるスレッドセーフ

マルチコア時代において、データ構造にスレッドセーフはつき物です。
というかもう殆ど必須といってよいです。
コレクションクラスでは、非常に簡単にスレッドセーフなオブジェクトを得ることができます。

System.Collections.ArrayList arrayList
    = new System.Collections.ArrayList();

// スレッドセーフなラッパーを行う。
arrayList = System.Collections.ArrayList.Synchronized(arrayList);

はい、なんとも楽勝なラッピングです。
ArrayList, Hashtable, Stack, Queue 等、多くのコレクションで対応しています。
楽なことはありがたいことです。