« [.NET] Generic の List を継承してはいけない。 …なんて、 誰が言いだしたんだろう? | トップページ | [C#] 代入式の値は右辺値 »

2009年4月17日 (金)

[.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] Generic の List を継承してはいけない。 …なんて、 誰が言いだしたんだろう? | トップページ | [C#] 代入式の値は右辺値 »

プログラミング」カテゴリの記事

-プログラミング ( わんくま )」カテゴリの記事

コメント

この記事へのコメントは終了しました。

トラックバック


この記事へのトラックバック一覧です: [.NET] そういうからには、 Collection<T> より List<T> の方が速いんだよね?:

» Generic List をインターフェースとして公開しちゃいけない理由 [菊池 Blog]
Generic List をインターフェースとして公開しちゃいけない理由 [続きを読む]

受信: 2009年4月19日 (日) 16時39分

« [.NET] Generic の List を継承してはいけない。 …なんて、 誰が言いだしたんだろう? | トップページ | [C#] 代入式の値は右辺値 »