質問
リストが空かどうかを判断するための (速度と読みやすさの両方を考慮した) 「最善の」方法は何ですか?リストが型であっても IEnumerable<T>
Count プロパティはありません。
今、私はこれの間で迷っています:
if (myList.Count() == 0) { ... }
この:
if (!myList.Any()) { ... }
私の推測では、2 番目のオプションは最初の項目を確認するとすぐに結果が返されるため、より高速であるのに対し、2 番目のオプション (IEnumerable の場合) はカウントを返すためにすべての項目にアクセスする必要があります。
そうは言っても、2 番目のオプションは読みやすいと思いますか?どちらが好みですか?それとも、空のリストをテストするより良い方法を思いつきますか?
編集 @lassevk の応答は、次のように、可能であればキャッシュされたカウントを使用するための少しの実行時チェックと組み合わせて、最も論理的であると思われます。
public static bool IsEmpty<T>(this IEnumerable<T> list)
{
if (list is ICollection<T>) return ((ICollection<T>)list).Count == 0;
return !list.Any();
}
解決
これを行うことができます:
public static Boolean IsEmpty<T>(this IEnumerable<T> source)
{
if (source == null)
return true; // or throw an exception
return !source.Any();
}
編集:基になるソースに実際に高速な Count プロパティがある場合、.Count メソッドを使用するだけでも高速になることに注意してください。上記の有効な最適化は、.Any() アプローチではなく、いくつかの基本型を検出し、単純にそれらの .Count プロパティを使用しますが、保証ができない場合は .Any() にフォールバックすることです。
他のヒント
あなたが落ち着いたと思われるコードに 1 つ小さな追加を加えます。もチェックしてください ICollection
, 、これは、廃止されていないジェネリック クラスによっても実装されているためです (つまり、 Queue<T>
そして Stack<T>
)。私も使います as
の代わりに is
それはより慣用的であり、 より速いことが示されています.
public static bool IsEmpty<T>(this IEnumerable<T> list)
{
if (list == null)
{
throw new ArgumentNullException("list");
}
var genericCollection = list as ICollection<T>;
if (genericCollection != null)
{
return genericCollection.Count == 0;
}
var nonGenericCollection = list as ICollection;
if (nonGenericCollection != null)
{
return nonGenericCollection.Count == 0;
}
return !list.Any();
}
LINQ 自体は、何らかの方法で Count() メソッドを中心に深刻な最適化を行っているに違いありません。
これには驚きましたか?私はそれを想像します IList
実装、 Count
要素の数を直接読み取るだけですが、 Any
をクエリする必要があります IEnumerable.GetEnumerator
メソッド、インスタンスを作成して呼び出す MoveNext
少なくとも一度は。
/@マットを編集:
IEnumerable の Count() 拡張メソッドが次のようなことを行っているとしか推測できません。
はい、もちろんそうです。これが私が言いたかったことです。実際に使用するのは、 ICollection
の代わりに IList
しかし結果は同じです。
簡単なテストを書いたので、これを試してください:
IEnumerable<Object> myList = new List<Object>();
Stopwatch watch = new Stopwatch();
int x;
watch.Start();
for (var i = 0; i <= 1000000; i++)
{
if (myList.Count() == 0) x = i;
}
watch.Stop();
Stopwatch watch2 = new Stopwatch();
watch2.Start();
for (var i = 0; i <= 1000000; i++)
{
if (!myList.Any()) x = i;
}
watch2.Stop();
Console.WriteLine("myList.Count() = " + watch.ElapsedMilliseconds.ToString());
Console.WriteLine("myList.Any() = " + watch2.ElapsedMilliseconds.ToString());
Console.ReadLine();
2 番目はほぼ 3 倍遅いです:)
スタックや配列、その他のシナリオでストップウォッチ テストを再度試してみると、実際にはリストのタイプに依存します。これは、Count が遅いことが判明したためです。
したがって、使用しているリストの種類によって異なると思います。
(念のため指摘しておきますが、リストに 2000 個以上のオブジェクトを入れましたが、他のタイプとは逆に、カウントは依然として高速でした)
List.Count
Microsoft のドキュメントによると、これは O(1) です。
http://msdn.microsoft.com/en-us/library/27b47ht3.aspx
だからただ使ってください List.Count == 0
クエリよりもはるかに高速です
これは、リストに何かが追加またはリストから削除されるたびに更新される Count というデータ メンバーがあるためです。 List.Count
取得するためにすべての要素を反復処理する必要はなく、データ メンバーを返すだけです。
複数の項目がある場合は、2 番目のオプションの方がはるかに高速です。
Any()
1 個のアイテムが見つかるとすぐに返されます。Count()
リスト全体を調べ続ける必要があります。
たとえば、列挙に 1000 個の項目があるとします。
Any()
最初のものをチェックしてから true を返します。Count()
列挙全体を走査した後は 1000 を返します。
これは、述語オーバーライドの 1 つを使用するとさらに悪化する可能性があります。Count() は、たとえ一致が 1 つしかなかったとしても、すべての項目をチェックする必要があります。
Any を使用することに慣れます。意味があり、読みやすいです。
注意点が 1 つあります。IEnumerable だけではなくリストがある場合は、そのリストの Count プロパティを使用してください。
@Konrad私が驚いたのは、テストで、リストを受け入れるメソッドにリストを渡していることです。 IEnumerable<T>
, そのため、ランタイムは Count() 拡張メソッドを呼び出して最適化することができません。 IList<T>
.
IEnumerable の Count() 拡張メソッドが次のようなことを行っているとしか推測できません。
public static int Count<T>(this IEnumerable<T> list)
{
if (list is IList<T>) return ((IList<T>)list).Count;
int i = 0;
foreach (var t in list) i++;
return i;
}
...言い換えれば、次のような特殊なケースのための実行時の最適化が少し施されています。 IList<T>
.
/EDIT @Konrad +1 mate - オンになっている可能性が高いという意見は正しいです ICollection<T>
.
さて、それではこれはどうでしょうか?
public static bool IsEmpty<T>(this IEnumerable<T> enumerable)
{
return !enumerable.GetEnumerator().MoveNext();
}
編集:誰かがすでにこの解決策を描いていることに今気づきました。Any() メソッドがこれを行うと述べましたが、自分で実行してみてはいかがでしょうか。よろしく
別のアイデア:
if(enumerable.FirstOrDefault() != null)
ただし、私は Any() アプローチの方が好きです。
これは、これを Entity Framework で動作させるために重要でした。
var genericCollection = list as ICollection<T>;
if (genericCollection != null)
{
//your code
}
Count() で確認すると、Linq はデータベース内で「SELECT COUNT(*)..」を実行しますが、結果にデータが含まれているかどうかを確認する必要があるため、Count() の代わりに FirstOrDefault() を導入することにしました。
前に
var cfop = from tabelaCFOPs in ERPDAOManager.GetTable<TabelaCFOPs>()
if (cfop.Count() > 0)
{
var itemCfop = cfop.First();
//....
}
後
var cfop = from tabelaCFOPs in ERPDAOManager.GetTable<TabelaCFOPs>()
var itemCfop = cfop.FirstOrDefault();
if (itemCfop != null)
{
//....
}
private bool NullTest<T>(T[] list, string attribute)
{
bool status = false;
if (list != null)
{
int flag = 0;
var property = GetProperty(list.FirstOrDefault(), attribute);
foreach (T obj in list)
{
if (property.GetValue(obj, null) == null)
flag++;
}
status = flag == 0 ? true : false;
}
return status;
}
public PropertyInfo GetProperty<T>(T obj, string str)
{
Expression<Func<T, string, PropertyInfo>> GetProperty = (TypeObj, Column) => TypeObj.GetType().GetProperty(TypeObj
.GetType().GetProperties().ToList()
.Find(property => property.Name
.ToLower() == Column
.ToLower()).Name.ToString());
return GetProperty.Compile()(obj, str);
}
これは、述語を考慮した Dan Tao の回答の私の実装です。
public static bool IsEmpty<TSource>(this IEnumerable<TSource> source, Func<TSource, bool> predicate)
{
if (source == null) throw new ArgumentNullException();
if (IsCollectionAndEmpty(source)) return true;
return !source.Any(predicate);
}
public static bool IsEmpty<TSource>(this IEnumerable<TSource> source)
{
if (source == null) throw new ArgumentNullException();
if (IsCollectionAndEmpty(source)) return true;
return !source.Any();
}
private static bool IsCollectionAndEmpty<TSource>(IEnumerable<TSource> source)
{
var genericCollection = source as ICollection<TSource>;
if (genericCollection != null) return genericCollection.Count == 0;
var nonGenericCollection = source as ICollection;
if (nonGenericCollection != null) return nonGenericCollection.Count == 0;
return false;
}
List<T> li = new List<T>();
(li.First().DefaultValue.HasValue) ? string.Format("{0:yyyy/MM/dd}", sender.First().DefaultValue.Value) : string.Empty;
myList.ToList().Count == 0
. 。それだけです
この拡張メソッドは私にとってはうまくいきます:
public static bool IsEmpty<T>(this IEnumerable<T> enumerable)
{
try
{
enumerable.First();
return false;
}
catch (InvalidOperationException)
{
return true;
}
}