我想比较给定类型的元素列表,看看哪个列表“更大”。
new BuiltInComparer<IEnumerable<int>>().Compare(
new[] {3,2,3},
new[] {1,2,3})
...将返回 1
new BuiltInComparer<IEnumerable<int>>().Compare(
new[] {1,2,3},
new[] {1,2,4})
...将返回 -1 等
有没有这样的内置比较器?
我想比较给定类型的元素列表,看看哪个列表“更大”。
new BuiltInComparer<IEnumerable<int>>().Compare(
new[] {3,2,3},
new[] {1,2,3})
...将返回 1
new BuiltInComparer<IEnumerable<int>>().Compare(
new[] {1,2,3},
new[] {1,2,4})
...将返回 -1 等
有没有这样的内置比较器?
我认为框架中没有内置任何内容 - 正如 Eric 所说,您没有提供比较标准。如果您的意思是“以自然的方式比较元素,并假设“缺失”元素小于任何现有元素”(即,如果它们尽可能相等,则较长的序列击败较短的子序列),那么这样的事情会做它:
public int SequenceCompare<T>(IEnumerable<T> source1, IEnumerable<T> source2)
{
// TODO: Parameter validation :)
// You could add an overload with this as a parameter
IComparer<T> elementComparer = Comparer<T>.Default;
using (IEnumerator<T> iterator1 = source1.GetEnumerator())
using (IEnumerator<T> iterator2 = source2.GetEnumerator())
{
while (true)
{
bool next1 = iterator1.MoveNext();
bool next2 = iterator2.MoveNext();
if (!next1 && !next2) // Both sequences finished
{
return 0;
}
if (!next1) // Only the first sequence has finished
{
return -1;
}
if (!next2) // Only the second sequence has finished
{
return 1;
}
// Both are still going, compare current elements
int comparison = elementComparer.Compare(iterator1.Current,
iterator2.Current);
// If elements are non-equal, we're done
if (comparison != 0)
{
return comparison;
}
}
}
}
更精致的版本:
public static class EnumerableExtensions
{
/// <summary>
/// Performs lexical comparison of 2 IEnumerable collections holding elements of type T.
/// </summary>
/// <typeparam name="T">Type of collection elements.</typeparam>
/// <param name="first">The first collection to compare.</param>
/// <param name="second">The second collection to compare.</param>
/// <returns>A signed integer that indicates the relative values of a and b:
/// Less than zero: first is less than second;
/// Zero: first is equal to second;
/// Greater than zero: first is greater than second.
/// </returns>
/// <remarks>
/// Can be called as either static method: EnumerableExtensions.Compare(a, b) or
/// extension method: a.Compare(b).
/// </remarks>
public static int Compare<T>(this IEnumerable<T> first, IEnumerable<T> second)
{
// If one of collection objects is null, use the default Comparer class
// (null is considered to be less than any other object)
if (first == null || second == null)
return Comparer.Default.Compare(first, second);
var elementComparer = Comparer<T>.Default;
int compareResult;
using (var firstEnum = first.GetEnumerator())
using (var secondEnum = second.GetEnumerator())
{
do
{
bool gotFirst = firstEnum.MoveNext();
bool gotSecond = secondEnum.MoveNext();
// Reached the end of collections => assume equal
if (!gotFirst && !gotSecond)
return 0;
// Different sizes => treat collection of larger size as "greater"
if (gotFirst != gotSecond)
return gotFirst ? 1 : -1;
compareResult = elementComparer.Compare(firstEnum.Current, secondEnum.Current);
} while (compareResult == 0);
}
return compareResult;
}
}
如果您使用的是 .NET 4(听起来不像是),我认为您可以使用Enumerable.Zip做一些聪明的事情。就像是:
var r = x.Zip(y, comparer.Compare).FirstOrDefault(c => c != 0);
尽管我现在看不到如何有效地处理较短的与较长的相同的情况,就目前而言。
编辑:如果您只是比较数组(或者不关心两次测量您的集合),那么我认为您可以简单地添加:
if (r == 0) {
r = int.Compare(x.Count(), y.Count());
}
您甚至可以将这些组合为:
var r = x.Zip(y, comparer.Compare)
.Concat(new [] { int.Compare(x.Count(), y.Count()) })
.FirstOrDefault(c => c != 0)
(如果您使用的是 .NET 3.5,那么添加一个 Zip 扩展方法,因为它易于编写并且在所有地方都非常有用!我不知道为什么它没有包含在最初的 Linq 版本中。)
没有内置的比较器。但是,这是经常出现的要求。我在SequenceComparer<T>
文章中详细介绍了该主题。这是一个简化的实现:
public class SequenceComparer<TElement> : Comparer<IEnumerable<TElement>>
{
private readonly IComparer<TElement> _elementComparer;
public SequenceComparer(IComparer<TElement> elementComparer = null)
{
_elementComparer = elementComparer ?? Comparer<TElement>.Default;
}
public override int Compare(IEnumerable<TElement> x, IEnumerable<TElement> y)
{
// Get enumerators to iterate over both sequences in sync.
using (IEnumerator<TElement> xEnumerator = x.GetEnumerator())
using (IEnumerator<TElement> yEnumerator = y.GetEnumerator())
{
// Advance both enumerators to their next element,
// until at least one passes the end of its sequence.
bool xMove, yMove;
while ((xMove = xEnumerator.MoveNext()) &&
(yMove = yEnumerator.MoveNext()))
{
// Compare the current pair of elements across the two sequences,
// seeking element inequality.
int elementComparison = _elementComparer.Compare(xEnumerator.Current, yEnumerator.Current);
if (elementComparison != 0)
return elementComparison;
}
// Determine the relative length of the two sequences based on the final values of xMove and yMove.
return xMove.CompareTo(yMove);
}
}
}