Advent LINQ (7): Count

Enumerable.Count()メソッドの特徴を考えてみる。ただ要素数を数えるだけなら、以下のような実装で良い筈だ。

public static class LinqExtensions
{
    public static int Count<T>(this IEnumerable<T> enumerable)
    {
        var count = 0;
        foreach (var value in enumerable)
        {
            count++;
        }
        return count;
    }
}

当たり前だが、これでは要素を全て列挙しなければ個数が分からない。例えば:

// 100000000個の値が入ったリスト
var values = Enumerable.Range(0, 100000000).ToList();

// 有意に時間がかかる
var count1 = LinqExtensions.Count(values);

// こちらは一瞬
var count2 = Enumerable.Count(values);

何故、Enumerable.Count()は速いのだろうか。それは、列挙子の実行時の型を見て最適化を行っているからだ。ICollectionインターフェイスを実装しているクラスであれば、Countプロパティを参照するだけで個数が識別出来る。この方法を利用するには、以下のようにキャストで判定する。

public static class LinqExtensions
{
    public static int Count<T>(this IEnumerable<T> enumerable)
    {
        // 列挙子がICollectionを実装していれば
        var collection = enumerable as ICollection;
        if (collection != null)
        {
            // 直接Countプロパティを参照する
            return collection.Count;
        }

        var count = 0;
        foreach (var value in enumerable)
        {
            count++;
        }
        return count;
    }
}

列挙子として配列を使用する場合、配列の長さを調べるにはLengthプロパティを参照する。しかし、配列は暗黙にICollectionインターフェイスを実装しているため、わざわざ配列かどうかを確認しなくても、上記コードがそのまま使える。

投稿者:

kekyo

A strawberry red slime mold. Likes metaprogramming. MA. Bicycle rider. http://amzn.to/1SeuUwD