I'm looking for a better alternative for Enumerable.Count() == n
. The best I've been able to come up with is:
static class EnumerableExtensions
{
public static bool CountEquals<T>(this IEnumerable<T> items, int n)
{
if (n <= 0) throw new ArgumentOutOfRangeException("n"); // use Any()
var iCollection = items as System.Collections.ICollection;
if (iCollection != null)
return iCollection.Count == n;
int count = 0;
bool? retval = null;
foreach (var item in items)
{
count++;
if (retval.HasValue)
return false;
if (count == n)
retval = true;
}
if (retval.HasValue)
return retval.Value;
return false;
}
}
class Program
{
static void Main(string[] args)
{
var items0 = new List<int>();
var items1 = new List<int>() { 314 };
var items3 = new List<int>() { 1, 2, 3 };
var items5 = new List<int>() { 1, 2, 3, 4, 5 };
var items10 = Enumerable.Range(0, 10);
var itemsLarge = Enumerable.Range(0, Int32.MaxValue);
Console.WriteLine(items0.CountEquals(3));
Console.WriteLine(items1.CountEquals(3));
Console.开发者_开发百科WriteLine(items3.CountEquals(3));
Console.WriteLine(items5.CountEquals(3));
Console.WriteLine(itemsLarge.CountEquals(3));
}
}
Can I do any better? Is there a way to generalize this even more—passing in the comparision?
You can use a combination of Take
and Count
to get rid of the loop entirely:
public static bool CountEquals<T>(this IEnumerable<T> items, int n)
{
var iCollection = items as System.Collections.ICollection;
if (iCollection != null)
return iCollection.Count == n;
return items.Take(n + 1).Count() == n;
}
Using Enumerable.Count
would be much better than your code above. It already optimizes for ICollection
internally.
That being said, if you must keep your extension, you could simplify the loop a bit:
int count = 0;
foreach (var item in items)
{
count++;
if(count > n)
return false;
}
return count == n;
What exactly do you mean by "better"? Faster? Easier?
Essentially what you seem to have done is written a specialized method that's optimized for one specific task. You mention generalizing it, but its performance advantage stems from the fact that it's so specific (assuming there is a performance advantage--methods like Count
are already tuned pretty hard for performance, and the compiler's pretty good at optimizing stuff like this).
Premature optimization is the root of all evil. If the performance of this particular operation is so important that it's worth replacing the twenty-odd-character expression xyz.Count() == abc
with dozens of lines of code, you may want to try other methods of increasing performance, like refactoring. For most situations, just the overhead from using managed code is going to dwarf the performance bonus you get (if any).
That being said -- if you have something like 10 million items, and your target count is a whole lot less, I'm pretty sure the below will short-circuit the iteration:
int count = 0;
var subset = items.TakeWhile(x => count++ < n + 1);
return count == n + 1;
Easy to read, easy to maintain, and probably just as fast.
精彩评论