環境について
Linqpad5で確認を行いました。
動機づけ
配列やList型はLengthやCountといったプロパティを使用することでコレクションの件数を取得し、N件以上であることを判定できます。
void Main()
{
int n = 3;
if (Array.Length > n)
{
Debug.WriteLine($"{nameof(Array)}は{n.ToString()}件以上です。");
}
else
{
Debug.WriteLine($"{nameof(Array)}は{n.ToString()}件未満です。");
}
if (List.Count > n)
{
Debug.WriteLine($"{nameof(List)}は{n.ToString()}件以上です。");
}
else
{
Debug.WriteLine($"{nameof(List)}は{n.ToString()}件未満です。");
}
}
private int[] Array => Collection.ToArray();
private List<int> List => Collection.ToList();
private IEnumerable<int> Collection
{
get
{
// 適当なデータです
yield return 0;
yield return 1;
yield return 2;
yield return 3;
yield return 4;
yield return 5;
}
}
IEnumerable<T>ではLINQのCount()メソッドを使用することでコレクションの件数を取得することは可能です。
void Main()
{
int n = 3;
if (Collection.Count() > n)
{
Debug.WriteLine($"{nameof(Collection)}は{n.ToString()}件以上です。");
}
else
{
Debug.WriteLine($"{nameof(Collection)}は{n.ToString()}件未満です。");
}
}
しかし、配列のLengthやリストのCountがプロパティへのアクセスに対して、LINQのCount()メソッドは全件走査が必要となります。
少ない件数では問題になりませんが、大量のデータを扱う場合には問題となる可能性があります。
対応
N = 1 の場合
1件以上であるかを判定する場合にはAny()メソッドを使えば判定できます。
1件目の要素にアクセスした時点で判定が終了するため、処理速度も安心できます。
void Main()
{
if (Collection.Any())
{
Debug.WriteLine($"{nameof(Collection)}は{1.ToString()}件以上です。");
}
else
{
Debug.WriteLine($"{nameof(Collection)}は0件です。");
}
}
N > 2 の場合
2件以上といった場合にはSkip()メソッドとAny()メソッドを組み合わせて行います。
void Main()
{
int n = 3;
if (Collection.Skip(n).Any())
{
Debug.WriteLine($"{nameof(Collection)}は{n.ToString()}件以上です。");
}
else
{
Debug.WriteLine($"{nameof(Collection)}は{n.ToString()}件未満です。");
}
}
この場合はSkip()メソッドによるn件の要素へのアクセスとAny()メソッドによる1件の要素へのアクセスの系 (n + 1) 件の要素へのアクセスが発生します。
(上記の例では (3 + 1) = 4件の要素へのアクセスが発生)
ListのCountプロパティと比べると遅いと思われますが、Collectionの件数に比べてNが小さい場合には全件走査が発生するCount()メソッドに比べて高速に動作できると考えられます。
補足
N = 1 と N > 2 の2つのパターンに分けましたが、Skip()メソッドを使う方法は N = 1 の場合にも有効です。
ですが、N = 1 の判定としては冗長で、使うことは無いと思われるため、分けて記載をさせていただきました。
可読性は……
やはりCount()メソッドを使った方が直観的に判定したいことが分かるので可読性はあると思います。
Count()メソッドでは速度上の懸念がある場合にチームで合意をして使うのが良いかもしれません。
LINQの様に拡張メソッドとして定義しておくと多少は可読性が向上するかもしれません。