以下代码在小型数据集上运行良好。但是,GetMatchCount和BuildMatchArrary在大结果上非常缓慢。谁能推荐任何不同的方法,以节省处理时间?将数组写入文件会更好吗?列表一般都很慢而不是最好的选择吗?
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
public class Client
{
public int Id;
public string FirstName
{
get
{
var firstName = //<call to get from database via Id>
return firstName;
}
}
public string MiddleName
{
get
{
var middleName = //<call to get from database via Id>
return middleName;
}
}
public string LastName
{
get
{
var lastName = //<call to get from database via Id>
return lastName;
}
}
public string FullName
{
get
{
return FirstName + " " + MiddleName + " " + LastName;
}
}
public int GetMatchCount(IEnumerable<string> clientFirstNames, IEnumerable<string> clientMiddleNames, IEnumerable<string> clientLastNames)
{
var clientFullNames = BuildMatchArray(clientFirstNames, clientMiddleNames, clientLastNames);
return clientFullNames.Count(x => x == FullName);
}
public string[] BuildMatchArray(IEnumerable<string> clientFirstNames, IEnumerable<string> clientMiddleNames, IEnumerable<string> clientLastNames)
{
Debug.Assert(clientFirstNames.Count() == clientMiddleNames.Count() && clientMiddleNames.Count() == clientLastNames.Count());
var clientFullNames = new List<string>();
for (int i = 0; i < clientFirstNames.Count(); i++)
{
clientFullNames.Add(clientFirstNames.ElementAt(i) + " " + clientMiddleNames.ElementAt(i) + " " + clientLastNames.ElementAt(i));
}
return clientFullNames.ToArray();
}
}
答案 0 :(得分:1)
你在哪里获得这些字符串?如果您使用延迟序列,则每次调用Count()
时,您都必须迭代整个序列以计算序列中有多少个对象。如果IEnumerable<T>
确实是T[]
或List<T>
,那么Count()
已经过优化,只需调用Length
或Count
属性即可很贵。类似地,ElementAt
也非常低效并且迭代集合。因此,对于内存中的延迟序列,这种性能会很糟糕,但如果您从SQL或外部源流式传输结果,那么它将非常糟糕甚至可能不正确。
BuildMatchArray
的更高效的实现将是这样的:
public IEnumerable<string> ZipNames(IEnumerable<string> firsts,
IEnumerable<string> middles, IEnumerable<string> lasts)
{
using(var e1 = firsts.GetEnumerator())
using(var e2 = middles.GetEnumerator())
using(var e3 = lasts.GetEnumerator())
{
var stop = false;
while (!stop)
{
var hasNext1 = e1.MoveNext();
var hasNext2 = e2.MoveNext();
var hasNext3 = e3.MoveNext();
if (hasNext1 && hasNext2 && hasNext3)
{
yield return $"{e1.Current} {e2.Current} {e3.Current}";
}
else
{
stop = true;
Debug.Assert(!(hasNext1 || hasNext2 || hasNext3));
}
}
}
}
这只需要每个输入集合的一次迭代,并且不需要将元素复制到新的List<T>
。另一点需要注意的是,List<T>
以4个元素的容量开始,当它填满时,它会将所有元素复制到具有双倍容量的新列表。因此,如果你有一个很大的序列,你会复制很多次。
此实现与System.Linq.Enumerable.Zip
在您的情况下,您也不应该对您的序列进行ToArray
。这将需要另一次复制,并且可能是一个巨大的阵列。如果你只是将该数组发送到.Count(x => x == y)
,那么保持一个懒惰的IEnumerable
会更好,因为Count
懒惰地操作延迟序列并在其中传输数据并在它们看到时对元素进行计数,没有要求完整的集合存在于内存中。