[.NET] そういうからには、 Collection<T> より List<T> の方が速いんだよね?
[.NET] Generic の List を継承してはいけない。 …なんて、 誰が言いだしたんだろう? …の続き。
> 菊池さん
んー、Listの継承といえば
class Tree : List
とかでよくやってるけど…
やっぱり、 そうですよね。 ちょっとした補助メソッドを追加したりするために、 あるいは、 それこそ型を固定するためだけに継承したり。
> 渋木宏明(ひどり) さん
「何故だめ(と言う人がいる?)なのか」を調べなくっちゃね、てことで。
前の記事を書くときに、 少しぐぐってみたんです。 継承しちゃダメ、 と書いている blog が何件も見つかりましたが、 しかし、 出所は分かりませんでした。 ( ので、 ああいうタイトルと結びになりました。 )
おそらくは、 MSDN の CA1002 の解説 「ジェネリック リストを公開しません」 のページに書いてある、 「System.Collections.Generic.List<T> は継承ではなくパフォーマンスを目的としたジェネリック コレクションである」 という文章に引き摺られちゃったんだと思います。 けれども、 そうじゃなくて、 別の理由があってダメだと言われてる可能性もあります。
> BlackB さん
>List はパフォーマンスを上げるために拡張性を犠牲にしてるから・・・
virtualにしない事でどれだけパフォーマンスは上がるんだろう?
virtual を付けるか付けないかで、 パフォーマンスの違いなんてほとんど無いと思います。
Code Analysis Team Blog で語られていることなどは、 ぶっちゃけ、 List<T> はパフォーマンスのために内部的にごにょごにょやってるので、 virtual にしてそのゴチャゴチャを公開したくない、 ってことなんじゃないかと思ってます。 f(^^;
ということで、 Collection<int> と List<int> の幾つかのメソッドで、 簡単にスピード計測をやってみました。
using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Linq;
namespace ListVsCollection {
class Program {
static void Main(string[] args) {
const int ITEMS_COUNT = 10000;
const int LOOP_COUNT = 1000;
GC.Collect();
{
System.Collections.ObjectModel.Collection<int> c
= new Collection<int>();
// Collection に追加
ShowTime("Collection - Add 開始");
for (int loop = 0; loop < LOOP_COUNT; loop++) {
c.Clear();
for (int i = 0; i < ITEMS_COUNT; i++)
c.Add(i);
}
ShowTime("Collection - Add 終了");
GC.Collect();
// Collection で検索
ShowTime("Collection - Where 開始");
for (int i = 0; i < ITEMS_COUNT; i++) {
int dummy = c.Where(item => item == i).Single();
}
ShowTime("Collection - Where 終了");
// Collection で位置指定
ShowTime("Collection - ElementAt 開始");
for (int loop = 0; loop < LOOP_COUNT; loop++) {
for (int i = 0; i < ITEMS_COUNT; i++) {
int dummy = c.ElementAt(i);
}
}
ShowTime("Collection - ElementAt 終了");
// Collection で削除
ShowTime("Collection - Remove 開始");
for (int i = 0; i < ITEMS_COUNT; i++)
c.Remove(i);
ShowTime("Collection - Remove 終了");
c.Clear();
c = null;
}
GC.Collect();
{
System.Collections.Generic.List<int> l = new List<int>();
// List に追加
ShowTime("List - Add 開始");
for (int loop = 0; loop < LOOP_COUNT; loop++) {
l.Clear();
for (int i = 0; i < ITEMS_COUNT; i++)
l.Add(i);
}
ShowTime("List - Add 終了");
GC.Collect();
// List で検索
ShowTime("List - Where 開始");
for (int i = 0; i < ITEMS_COUNT; i++) {
int dummy = l.Where(item => item == i).Single();
}
ShowTime("List - Where 終了");
// List で位置指定
ShowTime("List - ElementAt 開始");
for (int loop = 0; loop < LOOP_COUNT; loop++) {
for (int i = 0; i < ITEMS_COUNT; i++) {
int dummy = l.ElementAt(i);
}
}
ShowTime("List - ElementAt 終了");
// List で削除
ShowTime("List - Remove 開始");
for (int i = 0; i < ITEMS_COUNT; i++)
l.Remove(i);
ShowTime("List - Remove 終了");
}
Console.ReadKey();
}
private static DateTime _lastTime = DateTime.MinValue;
private static void ShowTime(string msg) {
DateTime nowTime = DateTime.Now;
Console.Write(nowTime.ToString("HH:mm:ss.fff"));
if (!string.IsNullOrEmpty(msg)) {
Console.Write(": ");
Console.Write(msg);
}
if (_lastTime > DateTime.MinValue) {
Console.Write("({0}mS) ",
(int)nowTime.Subtract(_lastTime).TotalMilliseconds);
}
Console.WriteLine();
_lastTime = nowTime;
}
}
}
結果の例を下に示します。
※ このくらいのループ回数では、 けっこうバラつきます。 あくまで参考までに。
16:32:29.611: Collection - Add 開始
16:32:30.361: Collection - Add 終了(750mS)
16:32:30.361: Collection - Where 開始(0mS)
16:32:35.783: Collection - Where 終了(5421mS)
16:32:35.783: Collection - ElementAt 開始(0mS)
16:32:36.408: Collection - ElementAt 終了(625mS)
16:32:36.408: Collection - Remove 開始(0mS)
16:32:36.517: Collection - Remove 終了(109mS)
16:32:36.517: List - Add 開始(0mS)
16:32:36.673: List - Add 終了(156mS)
16:32:36.673: List - Where 開始(0mS)
16:32:42.173: List - Where 終了(5500mS)
16:32:42.173: List - ElementAt 開始(0mS)
16:32:42.673: List - ElementAt 終了(500mS)
16:32:42.673: List - Remove 開始(0mS)
16:32:42.767: List - Remove 終了(93mS)
この結果から、 Add(), Remove(), ElementAt(), Whre() のうち、
・ Collection<T> に比べて、 List<T> は Add() が数倍速い。
・ Remove(), ElementAt(), Where<T>() は大差無い。
と言えるかと思います。
ほかにもパフォーマンスが大幅に向上しているメソッドもあるかもしれません。
# しまった、 Insert() も計測するべきだったか!? f(^^;
| 固定リンク
「プログラミング」カテゴリの記事
- 【.NET / Win8.1 ストアアプリ】 HttpClient で TLS 1.1 / 1.2 に対応するには(2018.06.17)
- 【VS2017 15.7pv2】 XAML のランタイム ツールに 「ヒートマップ」 が増えた(2018.03.28)
- 【.NET Core】 プロジェクトを作ると 「project.assets.json が見つかりません」 エラー(2018.02.10)
- 【#UWP】 ビットマップの表示色を変える (Win2D.uwp 経由で Direct2D を使う)(2017.08.23)
- 【#UWP】 CompactOverlay モード: Picture in Picture というか、「最前面に表示」するウィンドウを作る(2017.08.16)
「-プログラミング ( わんくま )」カテゴリの記事
- [わんくま名古屋] 第21回: Metro サンプルコード解説 (1/3)(2012.04.15)
- [わんくま名古屋] 第21回は 4月 14日 - Windows 8 Metro をやるよ~♪(2012.04.12)
- [わんくま東京] 第59回の資料を公開しました(2011.06.02)
- [わんくま東京] 第59回は 5月 28日 - 名古屋から侵略します♪(2011.05.23)
- [わんくま名古屋] 第17回は 4月 9日 - TDD ネタで喋ります(2011.04.03)
この記事へのコメントは終了しました。
コメント