« VB 文字列連結 高速化 | トップページ | VB たかがソート,されどSort »

2015年3月17日 (火)

VB ArrayList,LIST(Of T),配列 比較

VisualBasic6.0やVBAからVB.NETに移行してきた方はたいていの場合「配列使い」ですよね。しかしVB.NETだと,もはや配列は古い概念であって今は「ArrayList」なんだ,とか,いやいやArrayListも昔の話で今は「ジェネリック(List(Of T))」なんだよ,とかネットで散見されます。
このArrayList,List(Of T)が最初につまずくポイントの1つでなかなか身につきません。

ところで,CSVファイルを読み込むサンプルコードを改めて探していたところ,いつもお世話になっているサイトにいつものように載っていました。
CSV形式のファイルをDataTableや配列等として取得する

で,このサンプルコードの出力がArrayListになっています。
そのコードをあまり気にせず流用させて頂いていたのですが,CSVデータを計算させる自作クラスが元々,2次元配列前提だったためArrayList用に全部コードを直すか,またはデータをArrayListから2次元配列に変換するか,のどちらかの対応が必要になりました。
しかし,そもそもArrayListってもう古いんじゃなかったっけ?
ArrayListはオブジェクトとしてデータを扱うのでキャストがかかり遅くなるって書いてあるし。
となると,直すにしてもList(Of T)だろうし,でも,もしかして配列の方が高速なのでは?
と頭の中が収拾つかなくなったため,実験して方向性を決めることにしました。

まず10万行,5列,カンマ区切りのCSVファイルを用意し,ArrayList,List(Of T),ジャグ配列のそれぞれでファイルを読み込む時間を計測,さらに読み込んだArrayList,List(Of T),ジャグ配列,2次元配列(2次元配列は複製)のデータを変数に代入する時間をそれぞれ計測し比較してみました。

結果  (時間は秒)

項目  CSV読み込み  変数に代入  ArrayList比
ArrayList  1.883  1.4262  
List(Of T)  1.907  0.0066  217倍
ジャグ配列  1.858  0.0037  388倍
2次元配列  -  0.0057  248倍

CSV読み込みはArrayList,List(Of T),ジャグ配列とも同じプログラムコードでかつ3.3MByteもあるファイルを読み込んでいるためか差がないように見えます。対して変数への代入はArrayListだけがすごく遅いことが分かりました。

結論
1)ArrayListは使用しない。
2)検索やソート無しの単純な使用,かつ速度最優先ならジャグ配列。
3)上記2項以外は用途に応じて普通の配列かList(Of T)。

ArrayListは要素がObjectのために遅いのですが,逆に何でもありのObjectだからこそ利点になることもあるようです。
なお,上記計測は変数への代入時間ですが,2次元配列に代入した場合も計測しておりList(Of T),ジャグ配列,2次元配列とも倍程度の時間が掛かっていました。
それからDataTableと言うのもありますが,それも遅いですので用途次第での活用となります(自分でも検証済み)。
意外と遅い DataTable 、なので List  を使うと 5 倍早くなる

List(Of T)の最大の利点は要素数を気にしなくて良いことですが,改めて自分のプログラムコードを眺めると配列宣言で要素数を決め打ちしたり,結構ReDimしていたりで後々のメンテがいま一つ,かつロスっていますのでList(Of T)の活用は非常に有効です。


さて,食わず嫌いを止めて腕まくりしますか…


検証に使用したVisualBasicのソースコード
-----------------------------------------------------------
Imports System.Collections
Imports System.Collections.Generic

Public Class  Form1

Private Sub Button1_Click(sender As Object, e As EventArgs)_
Handles Button1.Click

Dim sw As New System.Diagnostics.Stopwatch()
Dim  csvFileName As String = "D:\Work\test.csv"
'3月16日,54315,3315,131315,10.432  のようなデータを10万行
Dim csvData1 As New ArrayList() 'ArrayList
Dim csvData2 As New List(Of String()) 'List(Of T)
Dim csvData3()() As String 'ジャグ配列
Dim loop1 As Integer
Dim loop2 As Integer
Dim dummyStr As String = ""

'ArrayListでCSV読み込み
sw.Start()
csvData1  = csvLoad_ArrayList(csvFileName)
sw.Stop()
Console.WriteLine(sw.Elapsed)
sw.Reset()

'List(Of T)でCSV読み込み
sw.Start()
csvData2 =  csvLoad_List(csvFileName)
sw.Stop()
Console.WriteLine(sw.Elapsed)
sw.Reset()

'ジャグ配列でCSV読み込み
sw.Start()
csvData3 =  csvLoad_Array(csvFileName)
sw.Stop()
Console.WriteLine(sw.Elapsed)
sw.Reset()

'ArrayListから変数に代入
sw.Start()
For loop1 = 0 To  csvData1.Count - 1
For loop2 = 0 To csvData1(loop1).length - 1
dummyStr =  csvData1(loop1)(loop2)
Next loop2
Next loop1
sw.Stop()
Console.WriteLine(sw.Elapsed)
sw.Reset()

'List(Of T)から変数に代入
sw.Start()
For loop1 = 0 To csvData2.Count - 1
For loop2 = 0 To  csvData2(loop1).Length - 1
dummyStr = csvData2(loop1)(loop2)
Next loop2
Next loop1
sw.Stop()
Console.WriteLine(sw.Elapsed)
sw.Reset()

'ジャグ配列から変数に代入
sw.Start()
For loop1 = 0 To csvData3.GetLength(0) - 1
For  loop2 = 0 To csvData3(loop1).Length - 1
dummyStr = csvData3(loop1)(loop2)
Next loop2
Next loop1
sw.Stop()
Console.WriteLine(sw.Elapsed)
sw.Reset()

'ジャグ配列から2次元配列を複製
Dim row As Integer = csvData3.GetLength(0)  - 1
Dim col As Integer = csvData3(0).Length - 1
Dim csvData4(row, col) As  String
For loop1 = 0 To csvData3.GetLength(0) - 1
For loop2 = 0 To  csvData3(loop1).Length - 1
csvData4(loop1, loop2) = csvData3(loop1)(loop2)
Next loop2
Next loop1
'2次元配列から変数に代入
sw.Start()
For loop1 = 0 To  csvData4.GetLength(0) - 1
For loop2 = 0 To csvData4.GetLength(1) - 1
dummyStr = csvData4(loop1, loop2)
Next loop2
Next loop1
sw.Stop()
Console.WriteLine(sw.Elapsed)
sw.Reset()

Application.Exit()

End  Sub

Private Function csvLoad_ArrayList(csvFileName As String)

'ArrayListでCSVファイル読み込み
Dim csvRecords As New ArrayList()
Dim tfp As New  FileIO.TextFieldParser(csvFileName, _
System.Text.Encoding.GetEncoding(932))
tfp.TextFieldType = FileIO.FieldType.Delimited
tfp.Delimiters = New String()  {","}
tfp.HasFieldsEnclosedInQuotes = True
tfp.TrimWhiteSpace = True

While Not tfp.EndOfData
Dim fields As String() = tfp.ReadFields()
csvRecords.Add(fields)
End While

tfp.Close()

Return csvRecords

End Function

Private Function csvLoad_List(csvFileName As String)

'List(Of T)でCSVファイル読み込み
Dim csvRecords As New List(Of String())
Dim  tfp As New FileIO.TextFieldParser(csvFileName, _
System.Text.Encoding.GetEncoding(932))
tfp.TextFieldType =  FileIO.FieldType.Delimited
tfp.Delimiters = New String() {","}
tfp.HasFieldsEnclosedInQuotes = True
tfp.TrimWhiteSpace = True

While  Not tfp.EndOfData
Dim fields As String() = tfp.ReadFields()
csvRecords.Add(fields)
End While

tfp.Close()

Return csvRecords

End Function

Private Function csvLoad_Array(csvFileName As String)

'List(Of T)でCSVファイルを読み込みジャグ配列に変換
Dim csvRecords()() As String
Dim  dummyRecords As New List(Of String())
Dim tfp As New  FileIO.TextFieldParser(csvFileName, _
System.Text.Encoding.GetEncoding(932))
tfp.TextFieldType = FileIO.FieldType.Delimited
tfp.Delimiters = New String()  {","}
tfp.HasFieldsEnclosedInQuotes = True
tfp.TrimWhiteSpace = True

While Not tfp.EndOfData
Dim fields As String() = tfp.ReadFields()
dummyRecords.Add(fields)
End While

tfp.Close()

csvRecords =  dummyRecords.ToArray
Return csvRecords

End Function

End Class

|

« VB 文字列連結 高速化 | トップページ | VB たかがソート,されどSort »

コメント

コメントを書く



(ウェブ上には掲載しません)




トラックバック

この記事のトラックバックURL:
http://app.cocolog-nifty.com/t/trackback/206107/61294143

この記事へのトラックバック一覧です: VB ArrayList,LIST(Of T),配列 比較:

« VB 文字列連結 高速化 | トップページ | VB たかがソート,されどSort »