うら干物書き

ゲームを作りたい!

【C#,LINQ】Count,LongCount~配列やリストの要素数がほしいとき~

 C#のLINQの関数であるCount(), LongCount()の使い方についてです。
 シーケンスの要素数を取得することが出来ます。
f:id:urahimono:20180617130125p:plain


この記事には.NET Framework 4.6.1を使用しています。

要素数を数える

 配列やリストの中にいくつ要素が詰め込まれているのか。
 配列の場合にはLengthプロパティを、リストの場合にはCountプロパティを使うことで要素数がわかります。
 LINQにもCount()というものがあり、IEnumerable<T>を継承したものなら使うことができます。

public static int Count<TSource>( this IEnumerable<TSource> source );
Enumerable.Count(TSource) メソッド (IEnumerable(TSource)) (System.Linq)

 早速試してみましょう。
Program.cs

using System.Linq;
using System.Collections;
using System.Collections.Generic;

public static class Program
{
    static void Main( string[] args )
    {
        int[]           intArray    = new int[] { 1, 5, 8, 12, 15, 16 };
        List<string>    stringList  = new List<string>() { "正一郎", "清次郎", "誠三郎", "征史郎" };

        int intCount    = intArray.Count();
        int stringCount = stringList.Count();

        // 結果発表
        System.Console.WriteLine( "intArrayの数:{0}", intCount );
        System.Console.WriteLine( "stringListの数:{0}", stringCount );
        // 入力待ち用
        System.Console.ReadKey();
    }

} // class Program

intArrayの数:6
stringListの数:4

 簡単に使えます。

条件に合った要素数を数える

 上記の使い方に加えて、数える要素に条件を追加することができます。

public static int Count<TSource>( this IEnumerable<TSource> source, Func<TSource, bool> predicate );
Enumerable.Count(TSource) メソッド (IEnumerable(TSource), Func(TSource, Boolean)) (System.Linq)

 特定のフラグが立っている要素のみを数えたり、特定の種類のデータのみを数えたり数場合に活用できます。
 以下の例では、偶数の値のみを数える場合と特定の文字の要素のみ数える処理を、ラムダ式を用いて実装しています。

Program.cs

using System.Linq;
using System.Collections;
using System.Collections.Generic;

public static class Program
{
    static void Main( string[] args )
    {
        int[]           intArray    = new int[] { 1, 5, 8, 12, 15, 16 };
        List<string>    stringList  = new List<string>() { "正一郎", "清次郎", "誠三郎", "征史郎" };

        // 偶数の要素の数を数えますよ。
        int intCount    = intArray.Count( value => value % 2 == 0 );
        // "三"の文字が入っている要素の数を数えますよ。
        int stringCount = stringList.Count( value => value.IndexOf( "三" ) >= 0 );

        // 結果発表
        System.Console.WriteLine( "intArrayの数:{0}", intCount );
        System.Console.WriteLine( "stringListの数:{0}", stringCount );
        // 入力待ち用
        System.Console.ReadKey();
    }

} // class Program

intArrayの数:3
stringListの数:1

 指定した条件の要素のみがカウントされました。

要素数が世界の人口数より多い場合

 さてさて、先ほど紹介したCount()なのですが、返り値の型はint型です。
 int型ということは、正数として使える値は、えーと……、20何億かぐらいでしょうか。
 では、それぐらいの要素数がある場合に、Count()を使うとどうなってしまうのでしょうか。
 試してみましょう。

Program.cs

using System.Linq;
using System.Collections;
using System.Collections.Generic;

public static class Program
{
    static void Main( string[] args )
    {
        // 0 から21億4748万3647個連続した整数のシーケンスを作るよ。
        IEnumerable<int> intSequenceA   = Enumerable.Range( 0, int.MaxValue );
        // -21億4748万3648個 から 21億4748万3647個連続した整数のシーケンスを作るよ。
        IEnumerable<int> intSequenceB   = Enumerable.Range( int.MinValue, int.MaxValue );

        // intSequenceAとintSequenceBを合体させた、すごい要素数のシーケンスを作成するよ。
        IEnumerable<int> intSequenceAB  = intSequenceA.Concat( intSequenceB );

        int count = 0;
        try
        {
            count   = intSequenceAB.Count();
        }
        catch( System.Exception i_exception )
        {
            System.Console.WriteLine( "例外:{0}", i_exception );
            // 入力待ち用
            System.Console.ReadKey();
            return;
        }

        // 結果発表
        System.Console.WriteLine( "intSequenceABの数:{0}", count );
        // 入力待ち用
        System.Console.ReadKey();
    }

} // class Program

例外:System.OverflowException: 算術演算の結果オーバーフローが発生しました。

 さすがに数十億の要素を作ろうとすると、処理に時間が掛かる……。
 ですが、結果が出ました。
 例外が発生するみたいですね。

 ということは、数十億単位の要素を扱う場合には、数を数えることは出来ないのでしょうか。

 そんな場合にはLongCount()というものがあります。
 これならば返り値はlong型なので、えーと……、一十百千……、兆の次ってなんだっけ……。
 ……とにかくとても大きな数値が扱えます。

public static long LongCount<TSource>( this IEnumerable<TSource> source );
Enumerable.LongCount(TSource) メソッド (IEnumerable(TSource)) (System.Linq)

 これを使って先ほど例外が起きた処理を書き直してみましょう。

Program.cs

using System.Linq;
using System.Collections;
using System.Collections.Generic;

public static class Program
{
    static void Main( string[] args )
    {
        // 0 から21億4748万3647個連続した整数のシーケンスを作るよ。
        IEnumerable<int> intSequenceA   = Enumerable.Range( 0, int.MaxValue );
        // -21億4748万3648個 から 21億4748万3647個連続した整数のシーケンスを作るよ。
        IEnumerable<int> intSequenceB   = Enumerable.Range( int.MinValue, int.MaxValue );

        // intSequenceAとintSequenceBを合体させた、すごい要素数のシーケンスを作成するよ。
        IEnumerable<int> intSequenceAB  = intSequenceA.Concat( intSequenceB );

        long count = 0;
        try
        {
            // intじゃ無理だ・・・、longならば!
            // count   = intSequenceAB.Count();
            count   = intSequenceAB.LongCount();
        }
        catch( System.Exception i_exception )
        {
            System.Console.WriteLine( "例外:{0}", i_exception );
            // 入力待ち用
            System.Console.ReadKey();
            return;
        }

        // 結果発表
        System.Console.WriteLine( "intSequenceABの数:{0}", count );
        // 入力待ち用
        System.Console.ReadKey();
    }

} // class Program

intSequenceABの数:4294967294

 こんどは例外が発生せず、きちんと数を数えることができました。
 LongCount()にもCount()同様、数える要素に条件をつけることができます。

public static long LongCount<TSource>( this IEnumerable<TSource> source, Func<TSource, bool> predicate );
Enumerable.LongCount(TSource) メソッド (IEnumerable(TSource), Func(TSource, Boolean)) (System.Linq)

Program.cs

using System.Linq;
using System.Collections;
using System.Collections.Generic;

public static class Program
{
    static void Main( string[] args )
    {
        // 0 から21億4748万3647個連続した整数のシーケンスを作るよ。
        IEnumerable<int> intSequenceA   = Enumerable.Range( 0, int.MaxValue );
        // -21億4748万3648個 から 21億4748万3647個連続した整数のシーケンスを作るよ。
        IEnumerable<int> intSequenceB   = Enumerable.Range( int.MinValue, int.MaxValue );

        // intSequenceAとintSequenceBを合体させた、すごい要素数のシーケンスを作成するよ。
        IEnumerable<int> intSequenceAB  = intSequenceA.Concat( intSequenceB );

        long count = 0;
        try
        {
            // -100万より大きい値の数を調べてみよう!
            count   = intSequenceAB.LongCount( value => value > -1000000 );
        }
        catch( System.Exception i_exception )
        {
            System.Console.WriteLine( "例外:{0}", i_exception );
            // 入力待ち用
            System.Console.ReadKey();
            return;
        }

        // 結果発表
        System.Console.WriteLine( "intSequenceABの数:{0}", count );
        // 入力待ち用
        System.Console.ReadKey();
    }

} // class Program

intSequenceABの数:2148483645

 ちなみにLongCount()long型で扱える正数の値を超える場合は例外が発生するみたいです。
 ……試しはしませんよ。
 数十億ですら、処理に結構時間が掛かったのに、それ以上桁の処理を扱った場合は、私のパソコンは二度と動かなくなるような気がします。

 ではあまり無茶な数の要素を扱わないようにして、Count()LongCount()を七兆回ほど使ってみてください。