Visual Basic クラスライブラリ詳解 |
Visual Basic 中学校 > クラスライブラリ詳解 > System.Collections >
ArrayList
完全限定名 | System.Collections.ArrayList |
基底クラス | System.Object |
インターフェイス | IList, ICollection, IEnumerable, ICloneable |
派生クラス | System.Windows.Forms.DomainUpDown.DomainUpDownItemCollectionなど |
使用頻度 | B |
バージョン | .NET Framework 1.0以上 (VB.NET2002以上) |
主な機能 | ・ArrayListはコレクションの一種 ・ArrayListは数値型のインデックスを使って項目を参照する。 |
ArrayListはコレクションの一種です。ArrayListを使うと複数の項目をまとめて管理できます。ArrayListは配列と似ていますが、あらかじめサイズを指定する必要 がなく項目の追加・削除が簡単に行えます。
ArrayListの要素の型はObject型ですので、文字列や数値以外にもテキストボックスや各種クラスなどさまざまな項目を管理することができます。各項目は0から始まる数値型のインデックスで区別されます。
ArrayListにはいろいろな使用方法がありますが、よくある使用例ではAddメソッドで項目を追加し、Itemプロパティで項目を取得します。項目には追加した順に0から始まるインデックスが割り当てられ、Itemプロパティではこのインデックスを指定して項目を取得します。ただし、Itemプロパティは省略可能なので、多くのプログラマは「.Item」とは記述しません。
以下はArrayListの単純な使用例です。AddメソッドとItemプロパティを使用しています。
Dim
Ar As New
ArrayList Dim St As String
Ar.Add("アウグストゥス") St = Ar(2) 'Itemプロパティにより、インデックス2の位置にある「カリギュラ」を取得する。 MsgBox(St) |
■リスト1
この例ではArrayList内容は次のようになります。
0 | アウグストゥス |
1 | ティベリウス |
2 | カリギュラ |
3 | クラウディウス |
4 | ネロ |
ArrayListクラスにはここで示した単純な使い方以外に項目の検索・変換・変更・列挙などを行うための様々な機能が用意されています。
これからArrayListの全ての機能を紹介しますが、ArrayListは機能が豊富なクラスですからこれを読む人ができるだけ迷わないように、またプログラマができるだけ楽にArrayListを扱えるように簡単な指針を示します。
まず、ArrayListは単純に使用する分にはAddメソッドとItemプロパティだけで制御できる点を覚えて おいてください。その他の機能はさしあたっては必要ありません。
また、ArrayListの要素が文字列型に変換可能な場合は、次のコードで簡単にArrayListに内容をListBoxに表示できます。
Dim Ar
As
New ArrayList Ar.Add("高慢と偏見") ListBox1.Items.AddRange(Ar.ToArray) |
■リスト2
デバッグ中はイミディエイトウィンドウに次のように入力することでArrayListの内容を一覧表示することもできます。
? ar.ToArray |
■リスト3
この例は変数arがArrayListである場合の例です。クイックウォッチを使ってArrayListの内容を確認することもできます。
こういった方法を効率的に使ってArrayListを快適に使いこなしてください。
それから、初級プログラマは忘れがちなのですがArrayListには文字列や数値以外にもテキストボックスやボタンなどさまざまなクラスを追加することができます。ここでは説明の便宜のために文字列を扱う例ばかりを紹介していますが、文字列以外のものでもまとめて管理する場合にはArrayListが使えます。
ArrayListを作成する方法は次の通りです。
方法 | 説明 |
Newキーワードを使う | コンストラクタを呼び出してArrayListを生成します。 |
Clone | 既存のArrayListをコピーして、新しいArrayListを作成します。 |
Repeat | 初めから指定した内容を含むArrayListを作成します。 |
ここには思いつく方法をすべて掲げましたが、まだ他の方法があるかもしれません。また、ReadOnlyメソッドやFixedSizeメソッドなどを使ってラッパーを作成する方法もあります。
ArrayListを作成するもっとも一般的な方法はNewを使うことです。次の例は新しいArrayListを作成します。
Dim Ar As New ArrayList |
■リスト4
この時点では変数Arで表されるArrayListには1つも項目が追加されていないので、何らかの方法で項目を追加する必要があります。インスタンスを作成するときにコンストラクタを使って項目を追加することもできます。項目を追加する方法は後で説明します。
Cloneメソッドを使うと、既に存在するArrayListをコピーして新しいArrayListを作成することができます。
Dim
Ar1 As
New ArrayList(New
String() {"北京",
"ベルリン", "ダブリン", "リベリア"}) Dim Ar2 As ArrayList Dim St As String Ar2 = Ar1.Clone St = Ar2(1) 'ベルリン |
■リスト5
Cloneメソッドを使わないで単純に代入演算子 = を使うとAr1とAr2は同じコレクションの別名になるので注意が必要です。この場合一方への変更は他方への変更でもあります。これは参照型の通常の仕様通りです。
Repeatメソッドを使うと最初からいくつかの項目を持っているArrayListを作成することができます。項目の数および初期値を指定することができます。たとえば、次の例では100個の空文字からなるArrayListを作成します。
Dim Ar
As ArrayList Ar = ArrayList.Repeat("", 100) |
■リスト6
Repeatメソッドは共有メソッドなのでクラスから直接呼び出すことができます。
ArrayListに項目を追加する方法は次の通りです。
方法 | 説明 |
コンストラクタを使う | 初期化時に他の配列やコレクションの内容をコピーします。 |
Add | 末尾に項目を1つ追加します。 |
AddRange | 末尾に複数の項目を追加します。 |
Insert | 指定した位置に項目を1つ追加します。 |
InsertRange | 指定した位置に複数の項目を追加します。 |
一般的にはAddメソッドを使って1つずつ項目を追加していきます。配列などをまとめて要素として追加したい時はAddRangeメソッドを使います。
コンストラクタに別のコレクションを指定することで、その内容をコピーすることができます。各項目には元となるコレクションと同じインデックスが割り当てられます。
Dim St
As
String Dim Ar1 As New ArrayList Ar1.Add("徳川家康") Dim Ar2 As New ArrayList(Ar1) St = Ar2(1) '徳川秀忠 MsgBox(St) |
■リスト7
このとき、コンストラクタはICollectionインターフェイスを備えるクラスを受け入れます。コレクションを表すほとんどのクラスはICollectionを実装していますが、StringDictionaryなど一部のクラスはICollectionを実装していません。そのためStringDictionaryをコンストラクタの引数に渡してArrayListの項目を追加することはできません。
なお、配列(Array)もICollectionインターフェイスを実装していますから、次のようなコードも有効です。
Dim Ar1 As New ArrayList(New String() {"徳川家康", "徳川秀忠", "徳川家光"}) |
■リスト8
Addメソッドは項目を追加する最も一般的な方法です。各項目には追加した順に0からはじまるインデックスが割り当てられます。
Dim
Ar1 As
New ArrayList
Ar1.Add("徳川家康") St = Ar1(0) '徳川家康 MsgBox(St) |
■リスト9
AddRangeメソッドを使うと複数の項目を一度に追加することができます。AddRangeメソッドの引数には他のコレクションや配列などICollectionインターフェイスを備えるオブジェクトを指定します。各項目には順番にインデックスが割り当てられます。
Dim St
As
String
Dim Ar1
As
New ArrayList Dim Ar2
As
New ArrayList Ar1.AddRange(Ar2) St = Ar1(4) '源頼家 MsgBox(St) |
■リスト10
コード実行後Ar1の内容は次のようになります。
0 | 徳川家康 |
1 | 徳川秀忠 |
2 | 徳川家光 |
3 | 源頼朝 |
4 | 源頼家 |
5 | 源実朝 |
Insertメソッドを使うと指定した位置に項目を1つ追加することができます。位置の指定にはインデックスを使用します。位置を指定できる以外はAddメソッドと同じです。項目には指定したインデックスが割り当てられます。
Dim
Ar1 As
New ArrayList
Ar1.Add("徳川家康") Ar1.Insert(1, "ルイ16世") St = Ar1(2) '徳川秀忠 MsgBox(St) |
■リスト11
コード実行後Ar1の内容は次のようになります。
0 | 徳川家康 |
1 | ルイ16世 |
2 | 徳川秀忠 |
3 | 徳川家光 |
InsertRangeメソッドを使うと指定した位置に複数の項目を挿入することができます。位置の指定にはインデックスを使用します。引数にはやはりICollectionを備えたオブジェクトを指定します。ですから他のコレクションや配列を指定した位置に挿入できるということになります。位置を指定できる以外はAddRangeメソッドと同じです。項目には指定したインデックスから順にインデックスが割り当てられます。
Dim St
As
String
Dim Ar1
As
New ArrayList Ar1.Add("徳川家康") Ar1.Add("徳川秀忠") Ar1.Add("徳川家光") Dim Ar2
As
New ArrayList Ar1.InsertRange(2, Ar2) St = Ar1(2) '源頼朝 MsgBox(St) |
■リスト12
コード実行後Ar1の内容は次のようになります。
0 | 徳川家康 |
1 | 徳川秀忠 |
2 | 源頼朝 |
3 | 源頼家 |
4 | 源実朝 |
5 | 徳川家光 |
ArrayListから項目を削除する方法は次の通りです。
方法 | 説明 |
Clear | すべての項目を削除します。 |
Remove | 値を指定して項目を1つ削除します。 |
RemoveAt | インデックスを指定して項目を1つ削除します。 |
RemoveRange | 複数の項目を削除します。 |
ClearメソッドはArrayListのすべての項目を削除します。このメソッドは引数なしで簡単に使用できます。
Ar.Clear() |
■リスト13
Removeメソッドを使うと指定した項目を削除できます。Removeメソッドの引数には既に追加されている項目と等価な値を指定します。
Dim St
As
String Dim Ar1 As New ArrayList Ar1.Add("徳川家康") Ar1.Remove("徳川秀忠") St = Ar1(1) '徳川家光 MsgBox(St) |
■リスト14
RemoveAtメソッドは、値ではなくインデックスを指定して項目を削除します。
Dim St
As
String Dim Items() As Integer = {121, 144, 169} Dim Ar1 As New ArrayList(Items) Ar1.RemoveAt(0) St = Ar1(0) '144 MsgBox(St) |
■リスト15
RemoveRangeメソッドは複数の項目を一度に削除します。RemoveRangeメソッドには削除する最初の項目のインデックスと、削除する項目の数を指定します。たとえば、RemoveRange(5, 3)とすると、インデックスが5, 6, 7の3つの項目が削除されます。
Dim
Count As
Integer Dim Ar1 As New ArrayList Ar1.Add("徳川家康") Ar1.RemoveRange(0, 2) Count = Ar1.Count '1 MsgBox(Count) |
■リスト16
ArrayListから項目を取得する方法は次の通りです。
方法 | 説明 |
CopyTo | ArralyListの内容をコピーした配列を作成します。 |
Item | 指定したインデックスの項目を取得します。 |
GetRange | ArrayListの一部分を使って別のArrayListを作成します。 |
ToArray | ArrayListの内容をコピーした配列を作成します。 |
ArrayListから項目を取得する最も一般的な方法はItemプロパティを使う方法です。
ItemプロパティはArrayListの個々の項目を表します。Itemプロパティの引数にはインデックスを指定します。
Dim St
As
String Dim Ar1 As New ArrayList Ar1.Add("徳川家康") St = Ar1.Item(1) '徳川秀忠 MsgBox(St) |
■リスト17
Itemプロパティは既定のプロパティなので省略することもできます。
Dim St
As
String Dim Ar1 As New ArrayList Ar1.Add("徳川家康") St = Ar1(1) '徳川秀忠 MsgBox(St) |
■リスト18
GetRangeメソッドは使い方に癖があるし、あまり便利ではないので使うことはお勧めできません。GetRangeメソッドはArrayListから一部分(または全部)を使って別のArrayListを作成します。GetRangeメソッドには対象となる部分の開始位置を示すインデックスと、項目の数を指定します。たとえば、GetRange(8, 4)とすると、インデックス8, 9, 10, 11の4つの項目からなるArrayListを作成します。ただし、この新しいArrayListは元のArrayListのコピーではない点に注意が必要です。
新しいArrayListは元のArrayListの呼び出し口に過ぎません。あくまで本体は元のArrayListです。GetRangeメソッドの呼び出し以降、元のArrayListに変更を加えるには呼び出し口となっている新しいArrayListを経由する必要があります。元のArrayListを直接変更すると新しいArrayListへのすべての操作は例外InvalidOperationExceptionを発生させます。
以下は単純なGetRangeメソッドの使用例です。
Dim St
As
String Dim Items() As String = {"徳川家康", "徳川秀忠", "徳川家光", "徳川家綱", "徳川綱吉"}
Dim Ar1
As
New ArrayList(Items) Ar2 = Ar1.GetRange(0, 3) '徳川家康, 徳川秀忠, 徳川家光 St = Ar2(2) '徳川家光 MsgBox(St) |
■リスト19
以下は注意すべきGetRangeメソッドの使用例です。GetRangeメソッドの使用後に新しいArrayListに変更を加えている点に注意してください。
Dim
St As
String Dim Items() As String = {"徳川家康", "徳川秀忠", "徳川家光", "徳川家綱", "徳川綱吉"}
Dim Ar1
As
New ArrayList(Items) Ar2 = Ar1.GetRange(1, 2)
'徳川秀忠,
徳川家光 St = Ar1(1) '元のArrayListの2番目の項目 = 源頼朝 MsgBox(St) |
■リスト20
上記のコードでArrayListの内容の移り変わりを模式化すると次の図のようになります。Ar2を変更すると、連動してAr1の対応する項目が変更されている点に注意してください。
Ar1の初期状態 |
Ar2の初期状態 |
→ |
変更後のAr2 |
変更後のAr1 |
ToArrayメソッドはArrayListの内容をコピーした配列を作成します。作成される配列は元のArrayListのコピーであるため、元のArrayListにいかなる変更を加えても配列には影響しません。ToArrayメソッドには作成する配列の型を指定します。型を指定しなかった場合Object型の配列が作成されます。
Dim St
As
String Dim Items() As String = {"徳川家康", "徳川秀忠", "徳川家光", "徳川家綱", "徳川綱吉"} Dim Ar1 As New ArrayList(Items) Dim Names() As String Names = Ar1.ToArray(GetType(String)) St = Names(0) '徳川家康 MsgBox(St) |
■リスト21
CopyToメソッドは既存の配列にArrayListの内容をコピーします。ToArrayメソッドと異なり、対象の配列は既にインスタンス化されていなければいけません。
次はCopyToメソッドの単純な使用例です。
Dim
St1 As
String Dim St2 As String Dim Items() As String = {"徳川家康", "徳川秀忠", "徳川家光"} Dim Ar1 As New ArrayList(Items) Dim Names() As String = New String() {"カンブリア紀", "オルドビス紀", "シルル紀", "デボン紀", "石炭紀", "ペルム紀"} Ar1.CopyTo(Names) St1 = Names(0)
'徳川家康 MsgBox(St1 & vbNewLine & St2) |
■リスト22
この例では、地質年代(カンブリア紀、オルドビス紀、…)を表す配列に、徳川宗家を表すArrayListをコピーします。しかし、ArrayListには要素が3つしかないため、配列の4つ目以降の要素はそのまま温存されます。
コード実行後配列Namesの内容は次のようになります。
0 | 徳川家康 |
1 | 徳川秀忠 |
2 | 徳川家光 |
3 | デボン紀 |
4 | 石炭紀 |
5 | ペルム紀 |
CopyToメソッドに第2引数を指定して、配列のどの位置にコピーするか指定することもできます。
Dim
St1 As
String Dim St2 As String Dim Items() As String = {"徳川家康", "徳川秀忠", "徳川家光"} Dim Ar1 As New ArrayList(Items) Dim Names() As String = New String() {"カンブリア紀", "オルドビス紀", "シルル紀", "デボン紀", "石炭紀", "ペルム紀"} Ar1.CopyTo(Names, 1) St1 = Names(0)
'カンブリア紀 MsgBox(St1 & vbNewLine & St2) |
■リスト23
コード実行後配列Namesの内容は次のようになります。
0 | カンブリア紀 |
1 | 徳川家康 |
2 | 徳川秀忠 |
3 | 徳川家光 |
4 | 石炭紀 |
5 | ペルム紀 |
また、さらに複雑にArrayListのどの位置から、配列のどの位置にいくつの要素をコピーするか指定することもできます。
Dim
St1 As
String Dim St2 As String Dim Items() As String = {"徳川家康", "徳川秀忠", "徳川家光"} Dim Ar1 As New ArrayList(Items) Dim Names() As String = New String() {"カンブリア紀", "オルドビス紀", "シルル紀", "デボン紀", "石炭紀", "ペルム紀"} Ar1.CopyTo(1, Names, 1, 2) St1 = Names(0)
'カンブリア紀 MsgBox(St1 & vbNewLine & St2) |
■リスト24
コード実行後配列Namesの内容は次のようになります。
0 | カンブリア紀 |
1 | 徳川秀忠 |
2 | 徳川家光 |
3 | デボン紀 |
4 | 石炭紀 |
5 | ペルム紀 |
ArrayListから項目を検索する方法は次の通りです。
方法 | 説明 |
BinarySearch | 並び替え済みのArrayListから項目を検索します。 |
Contains | ArrayListに指定した項目が存在するか調べます。 |
IndexOf | ArrayListで指定した内容が出現する位置を前方から調べて取得します。 |
LastIndexOf | ArrayListで指定した内容が出現する位置を後方から調べて取得します。 |
通常はIndexOfメソッドを使用すれば事足ります。
Containsメソッドは単純に項目があるかないかを返します。
Dim Ar
As
New ArrayList(New
String() {"北京",
"ベルリン", "ダブリン", "リベリア"})
If
Ar.Contains("北京")
Then |
■リスト25
IndexOfメソッドは項目のインデックスを返します。項目が存在しない場合は -1 を返します。
Dim Ar
As
New ArrayList(New
Integer() {5,
12, 78, 6, 627, 12, 0}) Dim Index As Integer Index = Ar.IndexOf(12) 'Index = 1 となる。 |
■リスト26
IndexOfメソッドはコレクションの先頭から検索を開始するので、同じ値の項目が複数ある場合は初めの方の項目のインデックスを返します。IndexOfメソッドの他のオーバーロードを使用するとコレクションの中での検索開始位置と検索する項目の数を指定することもできます。
Dim Ar
As
New ArrayList(New
Integer() {5,
12, 78, 6, 627, 12, 0}) Dim Index As Integer '3番目(=78)から検索を開始する。 Index = Ar.IndexOf(12, 2) 'Index = 5 となる。 '3番目(=78)から3項目分を検索する。 Index = Ar.IndexOf(12, 2, 3) 'Index = -1 となる。 |
■リスト27
LastIndexOfメソッドはIndexOfメソッドと同じですがコレクションの末尾から先頭へ向かって検索する点だけが異なります。このため同じ値の項目があった複数ある場合は後の方の項目のインデックスを返します。
Dim Ar
As
New ArrayList(New
Integer() {5,
12, 78, 6, 627, 12, 0}) Dim Index As Integer Index = Ar.LastIndexOf(12) 'Index = 5 となる。 '前から3番目(=78)から前方へ向かって検索を開始する。Index = Ar.LastIndexOf(12, 2) 'Index = 1 となる。 '前から3番目(=78)から前方へ向かって3項目分を検索する。 Index = Ar.LastIndexOf(672, 2, 3) 'Index = 1 となる。 |
■リスト28
BinarySearchメソッドは並び替えてある項目の順番を検索します。たとえば、{5, 12, 78, 6, 627, 12, 0} という項目をArrayListに追加した場合、このままBinarySearchメソッドを使ったのでは結果は予測不可能なものとなり意味がありません。BinarySearhメソッドを使うには項目を順番に追加するかSortメソッド等を使ってあらかじめ並び替えておく必要があります。既定では並び順は昇順、つまり小さいもの順である必要があります。
Dim Ar
As
New ArrayList(New
Integer() {0, 2,
4, 6, 8}) Dim Index As Integer Index = Ar.BinarySearch(4) '2 |
■リスト29
BinarySearchメソッドは項目が存在しなかった場合は、検索対象の次に大きい項目のビットごとの補数を返します。ビットごとの補数を取得するには Not演算子を使うだけですから、どの項目が次に大きい項目なのか簡単に調べることができます。また、検索対象の項目より大きいものが存在しない場合は項目数のビットごとの補数を返します。
Dim Ar
As
New ArrayList(New
Integer() {0, 2,
4, 6, 8}) Dim Index As Integer Index = Ar.BinarySearch(5) '5を検索する If Index >= 0 Then'目的の項目を発見した場合 MsgBox(Index & "番目にあります。") ElseIf (Not Index) = Ar.Count Then '目的の項目がなかった場合で、どの項目も目的の項目より小さい場合 MsgBox("見つかりませんでした。どの項目も検索対象より小さい項目です。") Else '目的の項目がなかった場合で、目的の項目より大きい項目がある場合 Index = Not Index 'ビットごとの補数を取得 MsgBox("見つかりませんでした。" & Index & "番目の項目は検索対象より大きい項目です。") End If |
■リスト30
昇順以外の順番で並び替えをしている場合は、その並び替えを定義するクラスを用意すればあらゆる並び替えに対応した検索を行うことができます。たとえば、降順で並んでいる場合にBinarySearchメソッドを使うには降順の並び方を定義するIComparerインターフェイスの実装を用意して、BinarySearchメソッドを呼び出します。
Private
Sub
btnBinarySearch_Click(ByVal
sender As
System.Object, ByVal
e As
System.EventArgs) Handles
btnBinarySearch.Click Dim
Ar As
New ArrayList(New
Integer() {8, 6,
4, 2, 0}) Index = Ar.BinarySearch(4, DESC) 'Index = 2 となる End Sub |
'降順を定義するクラス。IComparerインターフェイスを実装している必要がある。 Public Class CompareDesc Implements IComparer Public Function Compare(ByVal x As Object, ByVal y As Object) As Integer Implements System.Collections.IComparer.Compare Return Comparer.Default.Compare(y, x) End Function End Class |
■リスト31
また、BinarySearchメソッドでは検索開始位置と検索個数を指定することもできます。
Dim Ar
As
New ArrayList(New
Integer() {0, 2,
4, 6, 8}) Dim Index As Integer '3番目の項目(=4)から3項目分検索して 0 を探す。 '0は見つからないので Index = -3 となる。 Index = Ar.BinarySearch(2, 3, 0, Comparer.Default) |
■リスト32
このオーバーロードは第4引数を省略できないので既定の並び順を使用している場合でも、並び順を定義するクラスを指定しなければなりません。昇順の場合はComparer.Defaultを指定します。その他の場合は独自に定義したクラスを指定します。
BinarySearchメソッドに関しては説明のわかりやすさを重視したために数値で構成されたコレクションの例で説明しましたが、文字などで構成されたコレクションでBinarySearchメソッドを使用することもできます。
ArrayListの項目を変更する方法は次の通りです。
方法 | 説明 |
Reverse | コレクションの並び順を逆転させます。 |
Sort | コレクションを既定の順序、または任意の順序に並び替えます。 |
SetRange | 指定した範囲の項目を別の内容に入れ替えます。 |
個々の項目を変更するにはItemプロパティで取得した項目に対して必要な変更を加えるだけです。
Dim
Ar As New
ArrayList Ar.Add("オクタヴィアヌス") '最初の項目を「アウグストゥス」に変更します。 |
■リスト33
この節では複数の項目を変更する方法を説明します。
Reverseメソッドは項目の並び順を反対にするメソッドで、ごく簡単に使用することができます。たとえば、{"あ", "い", "う"}と項目が並んでいる場合Reverseメソッドを使用すると{"う", "い", "あ"}の順に並び変わります。
Dim
Ar As
New ArrayList(New
String() {"あ",
"い", "う"}) Dim St As String Ar.Reverse() St = Ar(0) '"う" MsgBox(St) |
■リスト34
さらにReverseメソッドに引数を指定して並び順を反対にする範囲を指定することができます。
Dim
Ar As
New ArrayList(New
String() {"開始",
"イ", "ロ", "ハ", "終了"}) Dim St1 As String Dim St2 As String Ar.Reverse(1, 3) St1 = Ar(0)
'"開始" MsgBox(St1 & vbNewLine & St2) |
■リスト35
この例では2番目の項目から3個分つまり "イ", "ロ", "ハ" の部分だけを逆順に並び替えます。そのためReverseメソッド実行後のコレクションの内容は次のようになります。
0 | 開始 |
1 | ハ |
2 | ロ |
3 | イ |
4 | 終了 |
もっとのこのような複雑な並び替えが必要なシーンはまずないと言ってよいでしょう。また、このような並び替えが必要になるようなプログラムの構造に問題がある場合も少なくないでしょう。おそらくまず使わない機能の1つです。
Sortメソッドは既定では項目を文字コード順に整列させます。Sortメソッドを使うことにより、無造作に項目を追加して言った場合でも後で並び替えることができるわけです。整列してるコレクションはそのままリストボックス等に表示しても見栄えが良いですし、コレクションを使った複雑なアルゴリズムを自作する際に便利です。また、整列しているコレクションに対してはBinarySearchメソッドを使った検索も可能になります。
Dim Ar
As
New ArrayList Dim St1 As String Dim St2 As String
Ar.Add("山田耕作")
'サン Ar.Sort() St1 = Ar(0)
'山田耕作 MsgBox(St1 & vbNewLine & St2) |
■リスト36
文字コードは通常音読みの五十音順に並んでいるため、この例ではSortメソッド実行後、項目は {"山田耕作", "大中恩", "滝廉太郎", "平井康三郎", "團伊玖磨"の順に並びます。
0 | 山田耕作 |
1 | 大中恩 |
2 | 滝廉太郎 |
3 | 平井康三郎 |
4 | 團伊玖磨 |
純粋な音読みでは"團伊玖磨"が"平井康三郎"より前になるはずですが、"團"という字がちょっと難しい漢字である「第2水準漢字」というグループに属しているために読み方に関わらず他の第1水準漢字よりも後に並びます。なお、サンプルに採用した作曲家の方々の敬称を省略させていただきました。このサンプルに登場する方はどなたも素晴らしい音楽を作っていらっしゃる日本を代表する作曲家の方です。
元の並び順 | 1文字目の音読み | 1文字目の区分 | Sort後の順番 | Shift-JIS |
山田耕作 | 山 = サン | 第1水準漢字 | 1 | 8e52 |
滝廉太郎 | 滝 = タキ | 第1水準漢字 | 3 | 91ea |
大中恩 | 大 = ダイ | 第1水準漢字 | 2 | 91e5 |
團伊玖磨 | 團 = ダン | 第2水準漢字 | 5 | 9aa3 |
平井康三郎 | 平 = ヘイ | 第1水準漢字 | 4 | 95bd |
他の文字列比較メソッドがすべてそうであるように、このメソッドもカルチャの影響を受けます。カルチャが変わると並び替えの順序も変ります。カルチャについてはここでは説明しません。ここでは日本における既定のカルチャについてのみ説明しています。
さて、Sortメソッドではどのような順番に並び替えるのか指定することもできます。何も指定しなかった場合は既に説明したように五十音順(に似た順番)に並び変ります。他にも逆順に並べたり、数値順に並べたりなど自由に定義することができます。オリジナルな順番で並べるにはIComparerインターフェイスを実装し、オリジナルな並び順を定義するクラスを自作する必要があります。
ここではBinarySearchメソッドのサンプルでも紹介した、独自にCompareDescクラスを使って逆順に並び替える例を紹介します。
Private Sub
btnSort_Click(ByVal
sender As
System.Object, ByVal
e As
System.EventArgs) Handles
btnSort.Click Dim
Ar As
New ArrayList Ar.Add("Banana") Ar.Sort(New CompareDesc) St1 = Ar(0)
'Cat MsgBox(St1 & vbNewLine & St2) End Sub |
'降順を定義するクラス。IComparerインターフェイスを実装している必要がある。 Public Class CompareDesc Implements IComparer |
Public Function Compare(ByVal x As Object, ByVal y As Object) As Integer Implements System.Collections.IComparer.Compare Return Comparer.Default.Compare(y, x) End Function End Class |
■リスト37
このコードを実行するとArrayListの内容は次のようになります。
0 | Cat |
1 | Banana |
2 | Apple |
なお、Sortメソッドはコレクション全体を対象とするのではなくコレクションの一部分だけを対象とするように指定することもできます。
Dim
Ar As
New ArrayList(New
String() {"開始",
"み", "か", "ん", "終了"}) Dim St1 As String Dim St2 As String Ar.Sort(1, 2, Comparer.Default) St1 = Ar(0)
'"開始" MsgBox(St1 & vbNewLine & St2) |
■リスト38
このコードを実行するとArrayListの内容は次のようになります。
0 | 開始 |
1 | か |
2 | み |
3 | ん |
4 | 終了 |
この場合は第3引数にIComparerを実装するクラスを必ず指定しなければなりませんので、通常の並び順でよい場合はこのサンプルのようにComparer.Defaultを指定します。
SetRangeメソッドは指定した範囲の項目を別の内容に入れ替えます。
Dim
Ar As
New ArrayList(New
String() {"日曜日",
"Monday", "Tuesday", "水曜日"}) Dim St1 As String Dim St2 As String Ar.SetRange(1, New String() {"月曜日", "火曜日"}) St1 = Ar(0)
'日曜日 MsgBox(St1 & vbNewLine & St2) |
■リスト39
このコードを実行するとArrayListの内容は次のようになります。
0 | 日曜日 |
1 | 月曜日 |
2 | 火曜日 |
3 | 水曜日 |
この例では、はじめコレクションの2番目の項目と3番目の項目は英語ですが、SetRangeメソッドを使って日本語の項目と入れ替えています。SetRangeメソッドは第1引数に入れ替えを開始するインデックスを指定し、第2引数にICollectionインターフェイスを実装するクラス、つまり配列や多くのコレクションを指定します。このとき、元のコレクションの項目数を変えることはできないので、入れ替えようとしている要素数が元のコレクションの項目数をオーバーする場合はSystem.AugumentOutOfRangeExceptionが発生します。
SetRangeメソッドの第2引数はICollectionインターフェイスを実装するクラスならなんでも指定できるので配列の他にもArrayListやHashtableなどいろいろなものを指定することができます。とはいえ、Hashtableなどには項目の「順番」というものが存在しないので第2引数にHashtableを指定したときにどのような順番で項目が並ぶかは未詳です。
マルチスレッドでArrayListを処理する場合のために以下のメソッド・プロパティが用意されています。
方法 | 説明 |
IsSynchronized | ArrayListがスレッドセーフであるか確認します。 |
SyncRoot | SyncLockステートメントなどでArrayListをロックするために使用します。 |
Synchronized | スレッドセーフなArrayListを作成します。 |
ArrayListは複数のスレッドで同時に使用した場合にどのような結果になるか不定です。つまり、同じプログラムであっても意図した 通りの結果になる場合もあれば、意図したものとは違う結果になる場合もあります。
そこで、複数のスレッドでArrayListを操作する場合にはSyncRootオブジェクトを指定してSyncLockステートメントを使います。
SyncLock
Ar.SyncRoot Ar.Add("TestItem") End SyncLock |
■リスト40
ただし、この例ではこSyncLockブロック内に限ってスレッドセーフになるに過ぎません。
Synchronizedメソッドを使うと、全体的にスレッドセーフなArrayListを作成できます。
Dim ArSafe As ArrayList = ArrayList.Synchronized(Ar) |
■リスト41
Synchronizedメソッドは実際にはArrayListを継承したSyncArrayListクラスを返します。このクラスはすべてのメソッドにSyncLockを適用したような動作をします。 ただし、列挙処理中は列挙処理全体でコレクションをロックしない限りスレッドセーフではありません。
IsSynchronizedプロパティを使うとArrayListがスレッドセーフかどうか判定できます。Synchronizedメソッドで作成したArrayListの場合Trueを返します。それ以外はFalseです。
固定サイズのArrayList、および読み取り専用のArrayListのために以下のメソッド・プロパティが用意されています。
方法 | 説明 |
IsFixedSize | ArrayListが固定サイズか確認する。 |
IsReadOnly | ArrayListが読み取り専用か確認する。 |
FixedSize | 固定サイズのArrayListまたは、IListラッパーを作る。 |
ReadOnly | 読み取り専用のArrayListまたは、IListラッパーを作る。 |
これらの機能は通常は使用しません。特殊な目的または綿密な計画がある場合にのみ使用します。
固定サイズのArrayListを作るにはFixedSizeメソッドを使用します。このメソッドは既存のArrayListを元に固定サイズのArrayListを作ります。また、ArrayListとは関係なく配列などのIListインターフェースを備えるオブジェクトを元に固定サイズのIListインターフェースを備えるオブジェクトを作ることもできます。
通常のArrayListを元に固定サイズのArrayListを作成するには次のようにします。
Dim Ar
As
New ArrayList(New
String() {"徳川家康",
"織田信長",
"豊臣秀吉"}) Dim arFixed As ArrayList arFixed = ArrayList.FixedSize(Ar) |
■リスト42
通常の配列から固定サイズのIListラッパーを作成するには次のようにします。
Dim
Names() As String =
New
String() {"徳川家康",
"織田信長",
"豊臣秀吉"} Dim FixedNames As IList FixedNames = ArrayList.FixedSize(Names) |
■リスト43
このように作成された固定サイズのラッパーは要素の追加・削除等はできなくなります。要素の変更はできます。固定サイズのラッパーに対して要素の追加・削除を行おうとするとSystem.NotSupportedExceptionが発生します。
ただし、元となっているArrayListや配列等に要素を追加・削除すると固定サイズのラッパーにもその変更が直ちに反映されます。
FixedSizeメソッドで作成されたArrayListは正確にはArrayListを継承するFixedSizeArrayListというクラスです。このクラスは単にArrayListに要素の追加と削除を禁止する機能を追加しただけのものです。FixedSizeメソッドで作成されたIListのラッパーは正確にはFixedSizeListというクラスです。
IsFixedSizeプロパティはArrayListまたはIListのラッパーが固定サイズであるか判定します。固定サイズである場合はTrueを返します。つまるところ、FixedSizeArrayListクラスのIsFixedSizeプロパティは常にTrueを返すという仕様になっています。
読み取り専用のArrayListは要素の追加・削除に加えて、変更・置換もできません。読み取り専用のArrayListに対して要素の追加・削除・変更を行おうとするとSystem.NotSupportedExceptionが発生します。これは読み取り専用のIListのラッパーについても同じことが当てはまります。
ReadOnlyメソッドは読み取り専用のArrayListを作ります。読み取り専用のIListのラッパーを作成することもできます。
通常のArrayListを元に読み取り専用のArrayListを作成するには次のようにします。
Dim Ar
As
New ArrayList(New
String() {"徳川家康",
"織田信長",
"豊臣秀吉"}) Dim arReadOnly As ArrayList arReadOnly = ArrayList.ReadOnly(Ar) |
■リスト44
通常の配列から固定サイズのIListラッパーを作成するには次のようにします。
Dim
Names() As String =
New
String() {"徳川家康",
"織田信長",
"豊臣秀吉"} Dim ReadOnlyNames As IList ReadOnlyNames = ArrayList.ReadOnly(Names) |
■リスト45
読み取り専用のArrayListまたはIListのラッパーの場合も元となっているオブジェクトが変更されると、その変更が直ちに反映されてます。読み取り専用のArrayListは正確にはArrayListを継承したReadOnlyArrayListというクラスです。読み取り専用のIListのラッパーはReadOnlyListというクラスです。
IsReadOnlyプロパティはArrayListまたはIListのラッパーが読み取り専用、つまりReadOnlyArrayListまたはReadOnlyListであるかを判定します。読み取り専用の場合はTrueを返します。
ArrayListでは列挙を行うためにGetEnumeratorメソッドが用意されています。
方法 | 説明 |
GetEnumerator | ArrayListの内容を列挙するための列挙子を返します。 |
列挙とはコレクションの要素を1つずつ取り出して処理することです。通常のFor 〜 Next文やDo 〜 Loop文を使って、要素を1つずつ処理することもできますが、より便利なFor Each文が使われることがほとんどです。GetEnumeratorメソッドはさらに複雑な列挙にも対応できます。ただし、プログラマの中にはFor EachよりもGetEnumeratorの方を好んで使用する人々もいます。
通常のFor Each文を使った列挙は次のようになります。この例ではコレクションの要素の中から数値に変換可能なものだけをListBoxに表示します。
Dim
Names() As String =
New
String() {"123",
"リンゴ",
"5",
"憲政記念館",
"ミカエル8世"} Dim Ar As New ArrayList(Names) Dim St As String For Each St In Ar If IsNumeric(St) Then ListBox1.Items.Add(St) End If Next |
■リスト46
数値に変換可能かどうか調べるためにはArrayListの要素を1つずつ検査する必要があり、ArrayListの要素を1つずつ取り出すためにFor Eachによる列挙を行っています。
このコードではArrayListのすべての要素が文字列型なので文字列型である変数Stを使用していますが、要素が文字列型でない場合はそれに対応する型の変数を用意する必要があります。
同じ例をGetEnumeratorメソッドを使用して書くと次のようになります。
Dim
Names() As String =
New
String() {"123",
"リンゴ",
"5",
"憲政記念館",
"ミカエル8世"} Dim Ar As New ArrayList(Names) Dim en As IEnumerator en = Ar.GetEnumerator Do
While en.MoveNext |
■リスト47
GetEnumeratorメソッドは列挙を制御するための列挙子を返します。ここでは変数enが列挙子となります。列挙子は必ずIEnumeratorインターフェースを実装しています。ここでは列挙子はArrayListEnumeratorSimpleというクラスになります。
列挙子についての説明はここでは行いませんが、操作は単純で、Currentプロパティで現在の値を取得し、MoveNextで次の値へ移動するカーソルのようなものを想像すれば十分です。MoveNexeメソッドは失敗するとFalseを返すので上の例のようにMoveNextがTrueを返す限り処理を続行すれば、すべての要素を列挙したことになります。
GetEnumeratorメソッドは列挙子を作成するときに、特定の部分のみを列挙するような列挙子を作成することもできます。
Dim
Names() As String =
New
String() {"123",
"リンゴ",
"5",
"憲政記念館",
"ミカエル8世"} Dim Ar As New ArrayList(Names) Dim en As IEnumerator en = Ar.GetEnumerator(1, 3) Do
While en.MoveNext |
■リスト48
この例では、2番目の要素から3つの項目のみを列挙する列挙子を作成しています。そのため、この例では"リンゴ", "5", "憲政記念館"のみが列挙の対象となります。このとき列挙子はArrayListEnumeratorというクラスになります。
方法 | 説明 |
Capacity | ArrayListの容量を設定・取得します。 |
Count | ArrayListの要素の数を取得します。 |
Adapter | 様々なIListインターフェースの実装に対し、ArrayListの機能を提供します。 |
TrimToSize | ArrayListの容量を必要最低限に切り詰めます。 |
CapacityプロパティはArrayListの容量を設定・取得します。ArrayListの容量とはArrayListに格納可能な要素の数です。Capacityプロパティを使うとこの容量を自由に設定・取得できます。ただし、現在の要素の数より容量を小さく設定しようとした場合はSystem.ArgumentOutOfRangeExceptionが発生します。
また、要素を追加したときに、容量が足りなくなると容量は自動的に拡張されるのでCapacityプロパティを使う場面はほとんどありません。容量ははじめは 0 です。この値はCapacityプロパティの他にコンストラクタで指定することもできます。
ArrayListに要素を1つ追加するとCapcityプロパティの値は自動的に 4 に設定されます。5つ目の要素を追加しようとすると、Capacityプロパティの値は 8 に拡張されます。このように容量が足りなくなるたびにCapacityプロパティは倍に拡張されます。
容量を拡張するためにはメモリを確保する必要があるので、連続していくつもの要素を追加する場合は動作が遅くなります。そこでパフォーマンスを重視する場合はあらかじめ十分な容量をCapacityプロパティで設定することによりパフォーマンスが向上します。
たとえば、次の例は通常の方法で要素を追加する例です。この例ではCapacityは自動的に拡張されます。プログラマはCapacityを意識する必要はありません。
Dim Ar
As
New ArrayList Dim i As Integer For i = 0 To 1000000 Ar.Add(i) Next |
■リスト49
一方、次の例は始めに明示的にCapacityを指定しています。容量を確保するのが最初の1回だけになるので、上記の例よりも若干パフォーマンスが向上します。
Dim Ar
As
New ArrayList Dim i As Integer Ar.Capacity = 1000000 For i = 0
To 1000000 |
■リスト50
コンストラクタでCapacityを指定する場合は次のようにします。
Dim Ar As New ArrayList(1000000) |
■リスト51
なお、今後ArrayListに要素を追加する可能性がほとんどない場合は、余分な容量を開放してパフォーマンスを向上させることができます。余分な容量を開放するにはCapacityプロパティの値をCountプロパティの値と一致させるか、TrimToSizeメソッドを使用します。
以下はTrimToSizeメソッドの使用例です。
Dim Ar
As
New ArrayList Ar.Add("A") MsgBox("TrimToSize使用前:Capacity = " & Ar.Capacity) Ar.TrimToSize() MsgBox("TrimToSize使用後:Capacity = " & Ar.Capacity) |
■リスト52
CountプロパティはArrayListの要素の数を示しています。何も要素がない状態ではCountプロパティの値は0です。
Dim Ar
As
New ArrayList(New
String() {"桜田門外の変",
"坂下門外の変",
"蛤御門の変"}) Dim Count As Integer Count = Ar.Count '3 MsgBox(Count) |
■リスト53
AdapterメソッドはIListインターフェイスを備えるさまざまなオブジェクトにArrayListの機能を提供する特殊なメソッドです。ArrayListにはここで説明しているようにたくさんの機能がありますが、これらの機能を他のIListインターフェイスを備えるオブジェクトでも使用可能にします。
たとえば、ListBoxのItemsプロパティはObjectCollectionクラスです。このクラスには項目を逆順に並び替えるReverseメソッドがありませんが、このクラスはIListインターフェイスを備えているのでArrayListのReverseメソッドを代わりに使用することができます。
Dim
AdaptedList As
ArrayList ListBox1.Items.Add("いいいい") AdaptedList = ArrayList.Adapter(ListBox1.Items) AdaptedList.Reverse() |
■リスト54
特殊な実装でない限りIListインタフェースを備えるすべてのオブジェクトに対してこのような処理が可能です。そのためArrayListの機能は非常に有用性の高いものとなります。ただし、Reverseメソッド、Sortメソッド、BinarySearchメソッドのような汎用性の高いメソッド以外の機能については意図どおりに動くかどうか注意が必要です。
また、Adapterメソッドで作成されるラッパーを経由した動作は本体(Adapterメソッドの引数に指定したオブジェクト)を直接操作した場合よりパフォーマンスが劣ります。
本体に対して変更を加えた場合、Adapterメソッドで作成されるラッパーにはその変更が直ちに適用されます。
なお、構文上当然ではありますが、上記の例を簡略化して次のように書くこともできます。
ListBox1.Items.Add("いいいい") ListBox1.Items.Add("ああああ") ListBox1.Items.Add("うううう") ArrayList.Adapter(ListBox1.Items).Reverse() |
■リスト55
資料:ArrayListのパブリックメンバ
名前 | 由来 | 読み方 | 機能 | |
Capacity | キャパシティ | 容量の取得・設定 | ||
Count | ICollection | カウント | 要素の数を取得 | |
IsFixedSize | IList | イズフィックスドサイズ | 固定サイズであるか判定 | |
IsReadOnly | IList | イズリードオンリー | 読み取り専用であるか判定 | |
IsSynchronized | ICollection | イズシンクロナイズド | スレッドの同期に対応しているか判定 | |
Item | IList | アイテム | 項目の取得 | |
SyncRoot | ICollection | シンクル−ト | マルチスレッドでロックするときに使用 | |
Adapter | アダプター | 他クラスでArrayListの機能を使う | ||
Add | IList | アド | 項目を追加 | |
AddRange | アドレンジ | 複数の項目を追加 | ||
BinarySearch | バイナリーサーチ | 項目を検索 | ||
Clear | IList | クリア | 項目をすべて削除 | |
Clone | ICloneable | クローン | 内容をコピー | |
Contains | IList | コンテインズ | 項目が存在するか検索 | |
CopyTo | コピートゥー | 一部分を配列にコピー | ||
Equals | Object | イコールズ | ||
FixedSize | フィックスドサイズ | 固定サイズのArrayListを作成 | ||
GetEnumerator | IEnumerable | ゲットエニュームレイター | 列挙子を取得 | |
GetHashCode | Object | ゲットハッシュコード | ||
GetRange | ゲットレンジ | 一部分を抜き出す | ||
GetType | Object | ゲットタイプ | ||
IndexOf | IList | インデックスオブ | 項目の位置を調べる | |
Insert | IList | インサート | 項目を挿入 | |
InsertRange | インサートレンジ | 複数の項目を挿入 | ||
LastIndexOf | ラストインデックスオブ | 項目の位置を調べる | ||
ReadOnly | リードオンリー | 読み取り専用のArrayListを作成 | ||
ReferenceEquals | Object | リファレンスイコールズ | ||
Remove | IList | リムーブ | 項目を削除 | |
RemoveAt | IList | リムーブアット | 指定した位置の項目を削除 | |
RemoveRange | リムーブレンジ | 複数の項目を削除 | ||
Repeat | リピート | 指定した内容のArrayListを作成 | ||
Reverse | リバース | 並び順を逆にする | ||
SetRange | セットレンジ | 一部分を変更する | ||
Sort | ソート | 並び替える | ||
Synchronized | シンクロナイズド | 同期したArrayListを作成 | ||
ToArray | トゥーアレイ | 配列に変換 | ||
ToString | Object | トゥーストリング | ||
TrimToSize | トリムトゥーサイズ | 容量を最低に設定 |