【意外と知らない】C#とLISTでよく使うメソッドとLINQ

C#入門
この記事は約7分で読めます。

例えば画面やファイルからの入力など、処理が終わるまで個数が確定できないものから配列データを作る手段として、Listが良く使われます。

プログラムでは頻繁に登場する使用頻度の極めた高いクラスですが、豊富な機能の中でごく一部しか使っていなかったりしませんか?

そこで今回は、Listを使ってやりたいことに焦点を当て、Listに実装されているメソッドを使った方法と、Linqを使った方法について紹介したいと思います。

また初心者にとっても、Listに関する一通りの知識を最初に理解しておくほうが、後々のプログラミングが書きやすくなりますので、是非この記事を活用して下さい。

Listで良く使われる機能一覧

まず最初に、Listで良く使われる機能を一覧に纏めました。

Listに実装されているメソッドを使う方法と、Listに対してLinqを使う方法の2通りを載せて言いますので、好みに合わせて使ってみて下さい。

機能(やりたいこと)メソッドを使う場合Linqを使う場合補足説明
要素の個数を取得するmylist.Countmylist.Count()
mylist.Count(ラムダ式)
✔100未満を返す場合、
mylist.Count(i=> i < 100)
✔ラムダ式を省略した場合、mylist.Countと同じ
先頭の要素を取り出すmylist[0]  mylist.First()
最後の要素を取り出すmylist[mylist.Count – 1] mylist.Last()
指定した位置の要素を取り出すmylist[要素の番号]該当なし
要素を1つ追加するmylist.Add(値)該当なし
複数の要素を一括して追加するmylist.AddRange(配列)該当なし
指定した位置にある要素を削除するmylist.RemoveAt(位置)該当なし同じ値が複数存在する場合
最初に見つかった値のみ
削除される
指定した要素を削除するmylist.Remove(値)該当なし
指定した要素が含まれるか確認するmylist.Contains(値)該当なし
指定した要素の位置を取得するmylist.IndexOf(値)該当なし同じ値が複数存在する場合
最初に見つかった値の位置が返される
指定した条件に一致する要素を検索するmylist.Find(ラムダ式)該当なし同じ値が複数存在する場合
最初に見つかった値が返る
要素の重複を無くした結果を取り出す該当なしmylist.Distinct()
指定した条件を満たす要素を取り出す該当なしmylist.Where(
ラムダ式
).ToList()
mylist.Where(i=> i == 100).ToList()
要素に任意の計算結果を適用して取り出す該当なしmylist.Select(
ラムダ式
).ToList()
mylist.Where(i=> i *3)
.ToList()
要素の合計を求める該当なしmylist.Sum()
要素の平均を求める該当なしmylist.Average()
要素の最大値を求める該当なしmylist.Max()
要素の最小値を求める該当なしmylist.Min()
リストを配列に変換する該当なしmylist.ToArray()
リストを辞書に変換する該当なしmylist.ToDictionary(
 keyのラムダ式,
 valueのラムダ式
)
リストの要素をKeyとValueにセットするなら mylist.ToDictionary(i=>i)

Valueの値を数値に変換
するなら mylist.ToDictionary(i=>i,i=>i.ToString())
要素の並びを逆順にするmylist.Reverse()該当なし
要素を昇順に並べ替えるmylist.Sort()mylist.OrderBy(
ラムダ式
)
昇順で並べ替える例
mylist.OrderBy(i=>i)
要素を降順に並べ替えるmylist.Sort()
mylist.Reverse()
mylist.OrderByDescending(
ラムダ式
)
降順で並べ替える例 mylist.ThenByDescending(i=>i)

ListのメソッドとLinqの速度比較

先ほど、好みに合わせて使ってくださいと申しましたが、大量のデータを扱ったり、ループの中で並べ替えやデータの取り出しを行う場合、ListのメソッドとLinqの速度差が気になるところです。

そこで、1万個の整数の要素を持つリストデータと、1万個の文字列の要素を持つリストデータを用意し、Listのメソッドを使った場合と、Linqを使った場合の処理速度を比較してみました。

上記データに対して、「100万回のループの中で処理を実行した際に掛かった時間を計測する」という行為を10回繰り返し、その平均値を秒で表記しています。

尚、このテストを行ったPCのスペックは、Core(TM) i5-9400 CPU @ 2.90GHz ,メモリ 32GBとなります。

計測結果は環境により異なるため、値を見るよりも、何倍遅いか(あるいは速いか)の方を参考にして頂ければと思います。

リストの件数を数えるCount

要素の個数をカウントする場合、Countプロパティを参照する場合に比べて、LinqのCount()を呼び出す方が2~3倍遅い結果になりました。

とは言え、ともに数ミリ秒程度であるため、この差はそれほど気にする必要は無さそうです。

しかし、Countの引数にラムダ式を使って、指定した条件の数をカウントした場合は、約1万~2万倍遅い結果になりました。

また、要素の型が文字列であった場合、更に1.5倍遅くなるようです。

データ型Listプロパティ
count
Linq
Count()
Linq
Count(i=> i >= 0)
整数(int)0.00310.007187.5621
文字列0.00310.0095108.8519

先頭と末尾のデータ取り出し

先頭の要素を取り出す場合、Linqを使うと10倍くらい遅くなりました。

また、先頭の要素を取り出すよりも、末尾の要素を取り出す方が時間が掛かるようです。

末尾の要素を取り出す場合、先頭の要素を取り出す時と比べて両者の差は少なく、2.5倍ほど遅くなる程度でした。

データ型Listの添え字指定
List[0]
Linq
First()
Listの添え字指定
List[Count-1]
Linq
Last()
整数(int)0.0040.01830.00700.0168
文字列0.0040.03100.00660.0307

昇順及び降順ソート

並べ替えについては、要素が整数だった場合に比べて、文字列だった場合は223倍遅くなりました。

また、昇順より降順の方が、更に1.5倍程度遅くなっています。

昇順、降順のどちらにおいても、Linqの方が処理速度が9倍程度早くなるので、処理速度を稼ぎたい場合はLinqによる並べ替えを積極的に利用した方が良さそうです。

データ型Listメソッド
Sort
Linq
OrderBy
Listメソッド
Sort&Revers
Linq
OrdrByDescending
整数(int)51.930.018489.3300.0162
文字列115960.0451185710.0427

整数の合計計算

最後に、forループ、foreachループ、Linqのsumを使った時の速度を計測してみました。

その結果、foreachが最も早く、次いで LinqのSum、最後に forループという結果になりました。

また、扱うデータが整数、実数のどちらであっても、処理速度にほとんど影響はありませんでした。

データ型forループforeachループLinq:Sum()
整数(int)0.80960.38920.6591
実数(double)0.82370.33890.6666

計測に使ったサンプルプログラムの例

以下のプログラムにおける forループ内の処理を適宜変更して計測しました。

まとめ

今回はListで良く使われる機能にを実現するために、Listに備わっているメソッドと、Linqを使った場合の両方について、一覧にまとめた結果を紹介しました。

また、ListのメソッドとLinqのどちらでも記述できる場合について、簡単ではありますが、両者の速度を比較し、グラフ化した結果を掲載しました。

どちらの場合も十分処理速度が速いので、少量のデータを使う場合はどちらを使っても問題ありませんが、大量のデータやループ処理の中で何度も行うケースにおいては、速度が速い方を使った方が無難です。

この記事が皆様のプログラミングの一助になれば幸いです。

タイトルとURLをコピーしました