我有这个模型
public class CPMC
{
public int CPMCId { get; set; }
public List<TPM> tpm = new List<TPM>();
public List<TPMC> tpmc = new List<TPMC>();
}
public class TPMC
{
public int Id { get; set; }
public Int64 Amount { get; set; }
public int PId { get; set; }
public Int64 PAmount { get; set; }
public int CPMCId { get; set; }
}
public class TPM
{
public int Type { get; set; }
public int Id { get; set; }
public Int64 Amount { get; set; }
public int VAT { get; set; }
public DateTime FromDate { get; set; }
public DateTime ToDate { get; set; }
public int CPMCId { get; set; }
}
此列表的数据是CPMCId的5k记录,内部每个子列表的50k记录,条件是
List<int> CPMCIdList = aPP.Select(x => Convert.ToInt32(x.CPMCId)).Distinct().ToList();
List<CPMC> cpl = (from ap in CPMCIdList
select new CPMC
{
CPMCId = ap,
tpm = tpml.Where(x=>x.CPMCId == ap).ToList(),
tpmc = tpmcl.Where(x=>x.CPMCId == ap).ToList()
}).ToList();
但是在List中填充数据需要花费很多时间。你们能为这个解决方案提供更好的工具吗? 提前致谢
答案 0 :(得分:2)
首先,让我们将您的问题减少到最小的情况:
您有以下类型:
public class A
{
public int Id { get; set; }
public List<B> bs = new List<B>();
public List<C> cs = new List<C>();
}
public class B
{
public int CPMCId { get; set; }
}
public class C
{
public int CPMCId { get; set; }
}
显然,您有一个A
,B
和C
的列表
List<A> as;
List<B> bs;
List<C> cs;
您要创建A
的
现在首先让我们来看看为什么你的解决方案很慢。
您正在做的是首先创建您想要的所有ID的列表,然后,对于每个ID,搜索所有匹配的记录。这意味着您正在为每个ID完全扫描子列表。这显然不是最佳的。
您正在寻找的操作在Outer Join
中称为SQL
。不幸的是,Linq没有开箱即用的等效操作。
所以我们自己就是这样。可以制作这种方法的通用版本,但这并不完全是直截了当的。我们要做的是按照CPMCId
对A和B进行排序,然后在A
s列表中获取具有相应ID的所有匹配记录:
IEnumerable<A> make_as(IEnumerator<B> ordered_bs, IEnumerator<C> ordered_cs, IEnumerator<int> ordered_ids) {
//make sure the current element of bs and cs is at the first element, not before it.
if(!ordered_bs.MoveNext() || !ordered_cs.MoveNext())
throw new ArgumentException("empty bs or cs");
while(ordered_ids.MoveNext()) {
nextid = ordered_ids.Current;
var a = new A(){
id = nextId;
};
//process the B's
while(ordered_bs.Current.CPMCId < nextid) //not in the list, skip it {
ordered_bs.MoveNext();
}
while(ordered_bs.Current.CPMCId == nextid) //matching, add to the list {
a.bs.add(ordered_cs.Current);
if(!orderd_bs.MoveNext()) break; //move bs forward. If b's is empty, we're done here
}
//do the same for the C's
while(ordered_cs.Current.CPMCId < nextid) {
ordered_cs.MoveNext();
}
while(ordered_cs.Current.CPMCId == nextid) {
a.cs.add(ordered_cs.Current);
if(!ordered_cs.MoveNext()) break;
}
yield return a;
}
}
var result = make_as(
bs.orderBy(b => b.PCMCId).GetEnumerator(),
cs.orderBy(c => c.PCMCId).GetEnumerator(),
as.Select(a => a.id).OrderBy(id => id).Distinct().GetEnumerator()
).ToList()
一些注意事项:
我的印象是,这是已经完成一些处理的解决方案的一部分。如果您知道自己需要所有ID,则根本不需要A
的原始列表,而nextId将是Current
的最低A
}和B
s
现在你很有可能在自己挖洞中遇到了一些漏洞。很有可能你可以更有效地 - 更优雅地 - 在你的代码中进一步“上游”。
最后一点,当B
列表或C
列表中没有元素时,此代码段不起作用。在这种情况下,一个简单的GroupBy就足够了。
答案 1 :(得分:2)
由于两个内部循环线性搜索(LINQ Where
运算符),您当前的实现具有O(K*N*M)
时间复杂度,其中K=CPMCIdList.Count
,N=tpml.Count
,{{1} }。
使用LINQ Group Join运算符可以将其简化为更快M=tpmcl.Count
,这些运算符在内部使用非常有效的基于散列的查找:
O(K+M+N)