ジェネリック・クラスで変わるC#とVBのコレクション:特集C#&VBジェネリック超入門(前編)(2/4 ページ)
この新機能を使えばコレクション・クラスの利用が安全かつ効率的に! ちょっと難しいC# 2.0やVB 2005の「ジェネリック」を丁寧に解説。
ジェネリックを使った新しいコレクション: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("さようなら")
型パラメータに「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) ' コンパイル・エラー
stringListオブジェクトはstring型専用なので、ほかの型を追加しようとするとコンパイル・エラーとなる。
コレクションに対する操作は、従来のArrayListクラスと同じようにして行えます。インデクサも用意されていて、この場合にはオブジェクトを取り出すのにキャストは不要です。これはインデクサがstring型を返すためです。
string greeting;
greeting = stringList[0]; // キャスト不要
Dim greeting As String
greeting = stringList(0) ' キャスト不要
stringListオブジェクトはstring型専用なので、インデクサもstring型のオブジェクトを返す。
もちろん、foreach文による順次処理もできます。この場合にもキャストは行われません。
foreach (string s in stringList) {
Console.WriteLine(s);
}
For Each s As String In stringList
Console.WriteLine(s)
Next
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
【コラム】 値型のコレクションでも高速
キャストが不要となる分、ジェネリックのコレクションは従来よりもパフォーマンスが上がりますが、コレクションで値型のオブジェクトを扱う場合にはさらに有用です。
例えば、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.