厳密に型指定されたジェネリック List<T> を反復処理する最善の方法は何ですか?
-
08-06-2019 - |
質問
C#.NET および VB.NET で強く型指定された汎用リストを反復処理する最善の方法は何ですか?
解決
C# の場合:
foreach(ObjectType objectItem in objectTypeList)
{
// ...do some stuff
}
VB.NET に対する回答 パープルアリ:
For Each objectItem as ObjectType in objectTypeList
'Do some stuff '
Next
他のヒント
IEnumerable の一般的な実装の場合、最善の方法は次のとおりです。
//C#
foreach( var item in listVariable) {
//do stuff
}
ただし、重要な例外があります。IEnumerable には、実際に foreach ループがコンパイルされる Current() および MoveNext() のオーバーヘッドが含まれます。
構造体の単純な配列がある場合:
//C#
int[] valueTypeArray;
for(int i=0; i < valueTypeArray.Length; ++i) {
int item = valueTypeArray[i];
//do stuff
}
より速いです。
アップデート
@Steven Sudit との議論 (コメントを参照) の後、私の最初のアドバイスは時代遅れか間違っている可能性があると思うので、いくつかのテストを実行しました。
// create a list to test with
var theList = Enumerable.Range(0, 100000000).ToList();
// time foreach
var sw = Stopwatch.StartNew();
foreach (var item in theList)
{
int inLoop = item;
}
Console.WriteLine("list foreach: " + sw.Elapsed.ToString());
sw.Reset();
sw.Start();
// time for
int cnt = theList.Count;
for (int i = 0; i < cnt; i++)
{
int inLoop = theList[i];
}
Console.WriteLine("list for : " + sw.Elapsed.ToString());
// now run the same tests, but with an array
var theArray = theList.ToArray();
sw.Reset();
sw.Start();
foreach (var item in theArray)
{
int inLoop = item;
}
Console.WriteLine("array foreach: " + sw.Elapsed.ToString());
sw.Reset();
sw.Start();
// time for
cnt = theArray.Length;
for (int i = 0; i < cnt; i++)
{
int inLoop = theArray[i];
}
Console.WriteLine("array for : " + sw.Elapsed.ToString());
Console.ReadKey();
そこで、すべての最適化を行ってリリース時にこれを実行しました。
list foreach: 00:00:00.5137506
list for : 00:00:00.2417709
array foreach: 00:00:00.1085653
array for : 00:00:00.0954890
そして、最適化を行わずにデバッグします。
list foreach: 00:00:01.1289015
list for : 00:00:00.9945345
array foreach: 00:00:00.6405422
array for : 00:00:00.4913245
したがって、かなり一貫しているように見えますが、 for
よりも速いです foreach
また、配列は汎用リストよりも高速です。
ただし、これは 1 億回の反復にわたるもので、最も速いメソッドと最も遅いメソッドの差は約 0.4 秒です。大規模なパフォーマンスが重要なループを実行している場合を除き、心配する必要はありません。
VB.NETの場合:
For Each tmpObject as ObjectType in ObjectTypeList
'Do some stuff '
Next
C#
myList<string>().ForEach(
delegate(string name)
{
Console.WriteLine(name);
});
匿名デリゲートは現在 VB.Net に実装されていませんが、C# と VB.Net の両方でラムダを実行できるはずです。
C#
myList<string>().ForEach(name => Console.WriteLine(name));
VB.ネット
myList(Of String)().ForEach(Function(name) Console.WriteLine(name))
Grauenwolf が指摘したように、ラムダが値を返さないため、上記の VB はコンパイルできません。他の人が提案しているように、通常の ForEach ループが現時点ではおそらく最も簡単ですが、いつものように、C# が 1 行で実行できることを実行するにはコードのブロックが必要です。
これが役立つ理由を示すありきたりな例を次に示します。これにより、IEnumerable が存在するスコープ以外の別のスコープからループ ロジックを渡すことができるため、望まない場合は公開する必要さえありません。
絶対パスにしたい相対 URL パスのリストがあるとします。
public IEnumerable<String> Paths(Func<String> formatter) {
List<String> paths = new List<String>()
{
"/about", "/contact", "/services"
};
return paths.ForEach(formatter);
}
したがって、次のように関数を呼び出すことができます。
var hostname = "myhost.com";
var formatter = f => String.Format("http://{0}{1}", hostname, f);
IEnumerable<String> absolutePaths = Paths(formatter);
あなたに与える "http://myhost.com/about", "http://myhost.com/contact"
等この特定の例では、これを達成するためのより良い方法があることは明らかですが、私は基本原則を説明しようとしているだけです。
リストの内部実装が分からない場合、一般的にリストを反復処理する最良の方法は foreach ループだと思います。foreach は IEnumerator を使用してリスト内を移動するため、オブジェクトからオブジェクトに移動する方法はリスト自体に依存します。
たとえば、内部実装がリンク リストの場合、単純な for ループは foreach よりもかなり遅くなります。
それは理にかなっていますか?
それはアプリケーションによって異なります。
- for ループ (効率が優先される場合)
- foreach ループまたは ForEach メソッド、どちらか意図をより明確に伝えるもの
何かが欠けているかもしれませんが、以下の例を使用すると、一般的なリストを反復処理するのは非常に簡単になるはずです。List<> クラスは IList インターフェイスと IEnumerable インターフェイスを実装しているため、基本的に必要な方法でこれらを簡単に反復処理できます。
最も効率的な方法は、for ループを使用することです。
for(int i = 0; i < genericList.Count; ++i)
{
// Loop body
}
foreach ループの使用を選択することもできます。
foreach(<insertTypeHere> o in genericList)
{
// Loop body
}