找到具有最大值属性的元素的速度更快
通常,要找到具有最大值属性的元素,我喜欢这样
var itemWithMaxPropValue = collection.OrderByDescending(x => x.Property).First();
但从性能的角度来看,这是好方法吗? 也许我应该做这样的事情?
var maxValOfProperty = collection.Max(x => x.Property); var itemWithMaxPropValue = collection .Where(x => x.Property == maxValueOfProperty).First();
两种解决方案效率都不高。 第一个解决方案涉及整个集合。 第二种解决方案需要遍历收集两次。 但是您可以一次性找到具有最大属性值的项目而无需对集合进行排序。 MoreLINQ库中有MaxBy扩展。 或者您可以实现相同的function:
public static TSource MaxBy(this IEnumerable source, Func selector) { // check args using (var iterator = source.GetEnumerator()) { if (!iterator.MoveNext()) throw new InvalidOperationException(); var max = iterator.Current; var maxValue = selector(max); var comparer = Comparer.Default; while (iterator.MoveNext()) { var current = iterator.Current; var currentValue = selector(current); if (comparer.Compare(currentValue, maxValue) > 0) { max = current; maxValue = currentValue; } } return max; } }
用法很简单:
var itemWithMaxPropValue = collection.MaxBy(x => x.Property);
排序是N * log (N)
而Max只有N
时间复杂度,因此Max
更快 。 您正在寻找的是Linq没有提供的ArgMax
function,所以我建议实现它,例如:
public static class EnumerableExtensions { public static T ArgMax(this IEnumerable source, Func map, IComparer comparer = null) { if (Object.ReferenceEquals(null, source)) throw new ArgumentNullException("source"); else if (Object.ReferenceEquals(null, map)) throw new ArgumentNullException("map"); T result = default(T); K maxKey = default(K); Boolean first = true; if (null == comparer) comparer = Comparer .Default; foreach (var item in source) { K key = map(item); if (first || comparer.Compare(key, maxKey) > 0) { first = false; maxKey = key; result = item; } } if (!first) return result; else throw new ArgumentException("Can't compute ArgMax on empty sequence.", "source"); } }
所以你可以简单地说
var itemWithMaxPropValue = collection .ArgMax(x => x.Property);
我会选择Max
因为它是专为此目的而设计的。 排序找到Max
似乎太多了。
另外,我不会使用Where
来寻找最大值,而是使用Single
– 因为我们需要的只是一个Single
值。
var maxValOfProperty = collection.Max(x => x.Property); var itemWithMaxPropValue = collection .Single(x => x.Property == maxValueOfProperty);
或者使用First
(如果集合包含最大值的重复)
var maxValOfProperty = collection.Max(x => x.Property); var itemWithMaxPropValue = collection .First(x => x.Property == maxValueOfProperty);
或者,使用MoreLINQ (由Kathi建议),你可以使用MaxBy
:
var itemWithMaxPropValue = collection.MaxBy(x => x.Property);
在Jon Skeet的回答中查看这篇文章 。
某些指定function下的最大元素也可以通过以下两个函数找到。
static class Tools { public static T ArgMax(T t1, T t2, Func f) where R : IComparable { return f(t1).CompareTo(f(t2)) > 0 ? t1 : t2; } public static T ArgMax(this IEnumerable Seq, Func f) where R : IComparable { return Seq.Aggregate((t1, t2) => ArgMax(t1, t2, f)); } }
上述解决方案的工作原理如下: ArgMax
的第一个重载将比较器作为参数,将T
两个实例映射到实现可比性的类型; 返回最多这些。 第二个重载将序列作为参数,并简单地聚合第一个函数。 对于我所知道的最大搜索,这是最通用的,框架重用和结构合理的公式; 通过改变第一个函数中的比较,可以以相同的方式实现搜索最小值。