徳島ゲーム開発ごっこ 技術ブログ

ゲームを作るために役に立ったり立たなかったりする技術を学んでいきます!

【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()を七兆回ほど使ってみてください。

LINQのリンク