ジェネリック・クラスで変わるC#とVBのコレクション特集C#&VBジェネリック超入門(前編)(2/4 ページ)

» 2006年02月11日 00時00分 公開
[遠藤孝信,デジタルアドバンテージ]

ジェネリックを使った新しいコレクション:Listジェネリック・クラス

 .NET Framework 2.0のクラス・ライブラリには、ジェネリックの仕組みを使ったリストである「Listジェネリック・クラス」が導入されています。このクラスは、新しいSystem.Collections.Generic名前空間に含まれています。

 Listジェネリック・クラスは、

  • C#の場合: List<T>クラス
  • VBの場合: List(Of T)クラス

として表記されます。C#の場合の不等号(山カッコ)、VBの場合のOfキーワードや、クラス名なのにカッコを付ける書き方は、ジェネリックのためにそれぞれの言語で新しく導入された記述方法です。

 そして、大文字の「T」は型パラメータ(タイプ・パラメータ)と呼ばれるもので、インスタンスの作成時には「T」の部分にリストの要素として扱いたい型を指定して記述します。

 例えば、List<T>クラスを文字列(string型)のリストとしてインスタンス化し、要素を追加するには次のように記述します。

List<string> stringList = new List<string>();

stringList.Add("こんにちわ");
stringList.Add("さようなら");

Dim stringList As New List(Of String)

stringList.Add("こんにちわ")
stringList.Add("さようなら")

Addメソッドによる要素の追加(List)

 型パラメータに「string」を指定することにより、このリストは格納される要素をstring型として扱います。つまり、stringListオブジェクトは文字列専用のコレクションとなります。

 このため、Addメソッドはパラメータとしてstring型のみが指定可能となります。よって、以下のような記述はコンパイル時にエラーとなります。

Uri site = new Uri("http://www.atmarkit.co.jp");
stringList.Add(site); // コンパイル・エラー

Dim site As New Uri("http://www.atmarkit.co.jp")
stringList.Add(site) ' コンパイル・エラー

Addメソッドによる要素の追加(List)
stringListオブジェクトはstring型専用なので、ほかの型を追加しようとするとコンパイル・エラーとなる。

 コレクションに対する操作は、従来のArrayListクラスと同じようにして行えます。インデクサも用意されていて、この場合にはオブジェクトを取り出すのにキャストは不要です。これはインデクサがstring型を返すためです。

string greeting;
greeting = stringList[0]; // キャスト不要

Dim greeting As String
greeting = stringList(0) ' キャスト不要

インデクサによる要素の取り出し(List)
stringListオブジェクトはstring型専用なので、インデクサもstring型のオブジェクトを返す。

 もちろん、foreach文による順次処理もできます。この場合にもキャストは行われません。

foreach (string s in stringList) {
  Console.WriteLine(s);
}

For Each s As String In stringList
  Console.WriteLine(s)
Next

foreach文による要素の列挙(List)
ArrayListクラスで可能だったコレクションの基本的な操作はListジェネリック・クラスでも可能だ。

 Listジェネリック・クラスが追加されたことにより、ArrayListクラスを使うケースはほとんどなくなったといえるでしょう。

 以上のコードをまとめたコンパイル可能なソース・コードを以下に示しておきます。

using System;
using System.Collections.Generic;

class GenericSample {
  void Sample() {
    // List<T>クラスのインスタンス化
    List<string> stringList = new List<string>();

    // 要素の追加
    stringList.Add("こんにちわ");
    stringList.Add("さようなら");

    string greeting;
    greeting = stringList[0]; // キャスト不要

    Uri site = new Uri("http://www.atmarkit.co.jp");
    // stringList.Add(site); // コンパイル・エラー

    // 各要素の列挙
    foreach (string s in stringList) {
       Console.WriteLine(s);
    }
  }

  static void Main() {
    GenericSample gs = new GenericSample();
    gs.Sample();
  }
}

Imports System
Imports System.Collections.Generic

Class GenericSample
  Sub Sample()
    ' String(Of T)クラスのインスタンス化
    Dim stringList As New List(Of String)

    ' 要素の追加
    stringList.Add("こんにちわ")
    stringList.Add("さようなら")

    Dim greeting As String
    greeting = stringList(0) ' キャスト不要

    Dim site As New Uri("http://www.atmarkit.co.jp")
    ' stringList.Add(site) ' コンパイル・エラー

    ' 各要素の列挙
    For Each s As String In stringList
      Console.WriteLine(s)
    Next
  End Sub
End Class

Module Module1
  Sub Main()
    Dim gs As New GenericSample
    gs.Sample()
  End Sub
End Module

Listジェネリック・クラスを使用したサンプル・プログラム

【コラム】 値型のコレクションでも高速

 キャストが不要となる分、ジェネリックのコレクションは従来よりもパフォーマンスが上がりますが、コレクションで値型のオブジェクトを扱う場合にはさらに有用です。

 例えば、ArrayListオブジェクトに値型であるint型(Integer型)の数値を追加する場合、ArrayListクラス内部では数値をobject型に変換する際にボックス化(ボクシング)が行われます。これがパフォーマンスを劣化させる原因となります。

 しかし、Listジェネリック・クラスでint型を指定した場合、コレクションはint型専用となります。このためボックス化は行われません。

 次のサンプル・プログラムは、数値をArrayListオブジェクトに追加する場合と、ジェネリックのListオブジェクトに追加する場合の処理時間を計測します。

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

public class GenericSample {
  static void Main() {
    DateTime start, end;

    // ArrayListクラスを使った場合
    ArrayList alist = new ArrayList();
    start = DateTime.Now;
    for (int i = 0; i < 20000000; i++) {
      alist.Add(i);
    }
    end = DateTime.Now;

    Console.WriteLine(end - start);
    // 出力例:00:00:05.6875000

    // Listジェネリック・クラスを使った場合
    List<int> intList = new List<int>();
    start = DateTime.Now;
    for (int i = 0; i < 20000000; i++) {
      intList.Add(i);
    }
    end = DateTime.Now;

    Console.WriteLine(end - start);
    // 出力例:00:00:00.6718750
  }
}

Imports System
Imports System.Collections
Imports System.Collections.Generic

Module Module1
  Sub Main()
    Dim startTime, endTime As DateTime

    ' ArrayListクラスを使った場合
    Dim alist As New ArrayList
    startTime = DateTime.Now
    For i As Integer = 1 To 20000000
      alist.Add(i)
    Next
    endTime = DateTime.Now

    Console.WriteLine(endTime - startTime)
    ' 出力例:00:00:05.6406250

    ' Listジェネリック・クラスを使った場合
    Dim intList As New List(Of Integer)
    startTime = DateTime.Now
    For i As Integer = 1 To 20000000
      intList.Add(i)
    Next
    endTime = DateTime.Now

    Console.WriteLine(endTime - startTime)
    ' 出力例:00:00:00.8750000
  End Sub
End Module

値型オブジェクトの追加にかかる時間を計測するサンプル・プログラム
数値をArrayListオブジェクトに追加する場合と、ジェネリックのListオブジェクトに追加する場合の処理時間を計測する。

 コード内のコメント文に示したように、ジェネリックを使用したList<int>クラスの方が、ArrayListクラスに比べて、(筆者の環境の場合では)約6〜8倍高速になっているのが分かります。

Copyright© Digital Advantage Corp. All Rights Reserved.

RSSについて

アイティメディアIDについて

メールマガジン登録

@ITのメールマガジンは、 もちろん、すべて無料です。ぜひメールマガジンをご購読ください。