Listクラス模倣チュートリアル

このチュートリアルでは、C#のList<T>クラスの基本的な機能を模倣して、自作クラスを実装しながら、リストの内部構造を理解していきます。対象はプログラミングを始めたばかりの方です。


目標

  • List<T>の内部構造を理解する。
  • 配列の動的な拡張や縮小の仕組みを学ぶ。
  • ジェネリクス(T)の基本概念を掴む。

1. 基本構造の作成

まず、クラスの雛形を作成します。

public class MyList<T>
{
    private T[] _items; // 内部でデータを保持する配列
    private int _count; // 現在の要素数

    // コンストラクタ
    public MyList()
    {
        _items = new T[4]; // 初期容量は4
        _count = 0;
    }

    // プロパティ: 要素数を取得
    public int Count
    {
        get { return _count; }
    }
}

解説

  • T: ジェネリクスを使用することで、任意の型を格納可能にしています。
  • _items: リストの内部データを保持する配列です。
  • _count: 現在リストに格納されている要素数を記録します。

2. 要素を追加する機能の実装

次に、リストに要素を追加するAddメソッドを作ります。

public void Add(T item)
{
    // 配列の容量が足りない場合は拡張
    if (_count == _items.Length)
    {
        Resize();
    }

    _items[_count] = item; // 配列に要素を格納
    _count++;              // 要素数を増やす
}

private void Resize()
{
    // 配列を2倍の容量に拡張
    T[] newArray = new T[_items.Length * 2];
    for (int i = 0; i < _items.Length; i++)
    {
        newArray[i] = _items[i];
    }
    _items = newArray;
}

解説

  • Addメソッド:
    • 配列が満杯の場合、Resizeメソッドで配列を拡張します。
    • 空きがある場合は、次の空き位置に値を格納し、カウントを増やします。
  • Resizeメソッド:
    • 配列を現在の2倍の大きさに拡張し、元の要素を新しい配列にコピーします。

3. 要素を取得する機能の実装

次に、要素を取得するGetメソッドと、インデクサを実装します。

public T Get(int index)
{
    if (index < 0 || index >= _count)
    {
        throw new ArgumentOutOfRangeException(nameof(index), "Index is out of range.");
    }
    return _items[index];
}

// インデクサ
public T this[int index]
{
    get { return Get(index); }
    set
    {
        if (index < 0 || index >= _count)
        {
            throw new ArgumentOutOfRangeException(nameof(index), "Index is out of range.");
        }
        _items[index] = value;
    }
}

解説

  • Getメソッド:
    • 指定されたインデックスが範囲外の場合に例外をスローします。
  • インデクサ:
    • Getメソッドを利用して、配列のようにアクセスできるようにします。

4. 要素を削除する機能の実装

次に、指定したインデックスの要素を削除するRemoveAtメソッドを実装します。

public void RemoveAt(int index)
{
    if (index < 0 || index >= _count)
    {
        throw new ArgumentOutOfRangeException(nameof(index), "Index is out of range.");
    }

    for (int i = index; i < _count - 1; i++)
    {
        _items[i] = _items[i + 1];
    }

    _items[_count - 1] = default(T); // 最後の要素を初期化
    _count--;                        // 要素数を減らす
}

解説

  • RemoveAtメソッド:
    • 削除した要素以降の要素を左に詰めます。
    • 最後の要素をデフォルト値にリセットし、要素数を1減らします。

5. テストコードで動作確認

実装が完了したら、以下のようなコードで動作確認を行いましょう。

MyList<int> myList = new MyList<int>();

myList.Add(10);
myList.Add(20);
myList.Add(30);
myList.Add(40);

Console.WriteLine("Count: " + myList.Count); // Count: 4
Console.WriteLine("Item at index 2: " + myList[2]); // Item at index 2: 30

myList.RemoveAt(1);
Console.WriteLine("After removing index 1: " + myList[1]); // After removing index 1: 30

myList[1] = 99;
Console.WriteLine("After setting index 1 to 99: " + myList[1]); // After setting index 1 to 99: 99

練習課題

  1. 要素が見つかった場合にインデックスを返すIndexOfメソッドを実装してみましょう。
  2. すべての要素を削除するClearメソッドを追加してみましょう。
  3. 実際のList<T>と比較して、足りない機能を考えてみましょう。

まとめ

このチュートリアルでは、List<T>クラスを模倣することで、動的配列の内部構造や基本的な操作方法を学びました。これを通して、データ構造やジェネリクスの理解を深め、さらに高度なプログラミングへと進む基礎を築きましょう。

C#

Posted by hidepon