SequenceEqual 有什么好处?
What is the benefit of SequenceEqual?
我在for循环中做了SequenceEqual和元素比较的比较
static void Main(string[] args)
{
//var myList = new List<short>();
//var anotherList = new List<short>();
var myList = new short[2000000];
var anotherList = new short[2000000];
for (int i = 0; i < 2000000; i++)
{
//myList.Add(5);
//anotherList.Add(5);
myList[i] = 5;
anotherList[i] = 5;
}
var watch = System.Diagnostics.Stopwatch.StartNew();
//for (int i = 0; i < 2000000; i++)
//{
// if (myList[i] != anotherList[i])
// break;
//}
bool isEqual = myList.SequenceEqual(anotherList);
watch.Stop();
var elapsedMs = watch.ElapsedMilliseconds;
Console.WriteLine(elapsedMs);
Console.Read();
}
myList和anotherList为数组时,直接比较执行4ms,SequenceEqual执行21ms。当myList和anotherList为列表时,直接比较执行13ms,SequenceEqual执行30ms
如果慢了那么多,在某些情况下使用它会有好处吗?我只能想到一个,省了好几行代码
更具体地研究 List
s,似乎很多减速来自 SequenceEqual
中使用通用 IEnumerator
而不是 [=11= 的通用实现] 特定版本,速度提高一倍以上。但是,如果您无论如何都要测试 List
,不妨直接在 for
循环中编写代码,而不是使用枚举。
请注意 IList
的测试要慢很多,因为 List
没有实现 IList<T>.operator[]
这意味着它调用 IList.operator[]
returns object
并导致装箱。
我也是特例 Array
s,因为 IList
界面比直接访问慢得多,同样是由于装箱。
当然,利用可用于已知类型的高速 Count
或 Length
可以使不等长比较比 SequenceEqual
快得多。 OTOH,如果前两个元素不相等,SequenceEqual
可以比我的函数更快。
这是我的 LocalSequenceEqual
:
public static bool LocalSequenceEqual<TSource>(this IEnumerable<TSource> first, IEnumerable<TSource> second, IEqualityComparer<TSource> comparer = null) {
if (first is ICollection<TSource> fc && second is ICollection<TSource> sc)
if (fc.Count != sc.Count)
return false;
var cmp = comparer ?? EqualityComparer<TSource>.Default;
if (first is TSource[] fa && second is TSource[] sa) {
for (int j1 = 0; j1 < fa.Length; ++j1)
if (!cmp.Equals(fa[j1], sa[j1]))
return false;
return true;
}
if (first is List<TSource> fl && second is List<TSource> sl) {
for (int j1 = 0; j1 < fl.Count; ++j1) {
if (!cmp.Equals(fl[j1], sl[j1]))
return false;
}
return true;
}
using (var e1 = first.GetEnumerator()) {
using (var e2 = second.GetEnumerator()) {
while (e1.MoveNext()) {
if (!(e2.MoveNext() && cmp.Equals(e1.Current, e2.Current)))
return false;
}
if (e2.MoveNext())
return false;
}
}
return true;
}
我在for循环中做了SequenceEqual和元素比较的比较
static void Main(string[] args)
{
//var myList = new List<short>();
//var anotherList = new List<short>();
var myList = new short[2000000];
var anotherList = new short[2000000];
for (int i = 0; i < 2000000; i++)
{
//myList.Add(5);
//anotherList.Add(5);
myList[i] = 5;
anotherList[i] = 5;
}
var watch = System.Diagnostics.Stopwatch.StartNew();
//for (int i = 0; i < 2000000; i++)
//{
// if (myList[i] != anotherList[i])
// break;
//}
bool isEqual = myList.SequenceEqual(anotherList);
watch.Stop();
var elapsedMs = watch.ElapsedMilliseconds;
Console.WriteLine(elapsedMs);
Console.Read();
}
myList和anotherList为数组时,直接比较执行4ms,SequenceEqual执行21ms。当myList和anotherList为列表时,直接比较执行13ms,SequenceEqual执行30ms
如果慢了那么多,在某些情况下使用它会有好处吗?我只能想到一个,省了好几行代码
更具体地研究 List
s,似乎很多减速来自 SequenceEqual
中使用通用 IEnumerator
而不是 [=11= 的通用实现] 特定版本,速度提高一倍以上。但是,如果您无论如何都要测试 List
,不妨直接在 for
循环中编写代码,而不是使用枚举。
请注意 IList
的测试要慢很多,因为 List
没有实现 IList<T>.operator[]
这意味着它调用 IList.operator[]
returns object
并导致装箱。
我也是特例 Array
s,因为 IList
界面比直接访问慢得多,同样是由于装箱。
当然,利用可用于已知类型的高速 Count
或 Length
可以使不等长比较比 SequenceEqual
快得多。 OTOH,如果前两个元素不相等,SequenceEqual
可以比我的函数更快。
这是我的 LocalSequenceEqual
:
public static bool LocalSequenceEqual<TSource>(this IEnumerable<TSource> first, IEnumerable<TSource> second, IEqualityComparer<TSource> comparer = null) {
if (first is ICollection<TSource> fc && second is ICollection<TSource> sc)
if (fc.Count != sc.Count)
return false;
var cmp = comparer ?? EqualityComparer<TSource>.Default;
if (first is TSource[] fa && second is TSource[] sa) {
for (int j1 = 0; j1 < fa.Length; ++j1)
if (!cmp.Equals(fa[j1], sa[j1]))
return false;
return true;
}
if (first is List<TSource> fl && second is List<TSource> sl) {
for (int j1 = 0; j1 < fl.Count; ++j1) {
if (!cmp.Equals(fl[j1], sl[j1]))
return false;
}
return true;
}
using (var e1 = first.GetEnumerator()) {
using (var e2 = second.GetEnumerator()) {
while (e1.MoveNext()) {
if (!(e2.MoveNext() && cmp.Equals(e1.Current, e2.Current)))
return false;
}
if (e2.MoveNext())
return false;
}
}
return true;
}