我有一些非常简单的代码,我试图使用LINQ而不是标准代码来快速运行(有很多这些小类型的调用遍布代码,这似乎会减慢速度)。 / p>
问题是这样的 - 我在LINQ之外有一个变量,LINQ查询的结果需要添加它。
原始代码如下所示
double total = 0
foreach(Crop c in p.Crops)
{
if (c.CropType.Type == t.Type)
total += c.Area;
}
return total;
这个方法在循环开始变大之前并不慢,然后在手机上变慢。这种代码可以转移到一个相对快速和简单的LINQ吗?
答案 0 :(得分:3)
看起来你可以使用sum :(编辑:我的语法错误)
total = (from c in p.Crops
where c.CropType.Type == t.Type
select c.Area).Sum();
或者以扩展方式格式:
total = p.Crops.Where(c => c.CropType.Type == t.Type).Sum(c => c.area);
至于那些说LINQ不会表现得更好的人,你的证据在哪里? (以下是基于post from Hanselman?我在linqpad中运行了以下内容:(您需要下载并参考nbuilder才能运行它)
void Main()
{
//Nbuilder is used to create a chunk of sample data
//http://nbuilder.org
var crops = Builder<Crop>.CreateListOfSize(1000000).Build();
var t = new Crop();
t.Type = Type.grain;
double total = 0;
var sw = new Stopwatch();
sw.Start();
foreach(Crop c in crops)
{
if (c.Type == t.Type)
total += c.area;
}
sw.Stop();
total.Dump("For Loop total:");
sw.ElapsedMilliseconds.Dump("For Loop Elapsed Time:");
sw.Restart();
var result = crops.Where(c => c.Type == t.Type).Sum(c => c.area);
sw.Stop();
result.Dump("LINQ total:");
sw.ElapsedMilliseconds.Dump("LINQ Elapsed Time:");
sw.Restart();
var result2 = (from c in crops
where c.Type == t.Type
select c.area).Sum();
result.Dump("LINQ (sugar syntax) total:");
sw.ElapsedMilliseconds.Dump("LINQ (sugar syntax) Elapsed Time:");
}
public enum Type
{
wheat,
grain,
corn,
maize,
cotton
}
public class Crop
{
public string Name { get; set; }
public Type Type { get; set; }
public double area;
}
结果对LINQ非常有利:
For Loop total:99999900000
For Loop Elapsed Time:25
LINQ总计:99999900000
LINQ经过的时间:17
LINQ(糖语法)总计:99999900000
LINQ(糖语法)经过时间:17
答案 1 :(得分:1)
优化此功能的主要方法是更改p
,这可能会也可能不会。
假设p
是P
,看起来像这样:
internal sealed class P
{
private readonly List<Crop> mCrops = new List<Crop>();
public IEnumerable<Crop> Crops { get { return mCrops; } }
public void Add(Crop pCrop)
{
mCrops.Add(pCrop);
}
}
(如果p
是类似List<Crop>
的.NET类型,则可以创建这样的类。)
您可以通过维护字典来优化循环:
internal sealed class P
{
private readonly List<Crop> mCrops = new List<Crop>();
private readonly Dictionary<Type, List<Crop>> mCropsByType
= new Dictionary<Type, List<Crop>>();
public IEnumerable<Crop> Crops { get { return mCrops; } }
public void Add(Crop pCrop)
{
if (!mCropsByType.ContainsKey(pCrop.CropType.Type))
mCropsByType.Add(pCrop.CropType.Type, new List<Crop>());
mCropsByType[pCrop.CropType.Type].Add(pCrop);
mCrops.Add(pCrop);
}
public IEnumerable<Crop> GetCropsByType(Type pType)
{
return mCropsByType.ContainsKey(pType)
? mCropsByType[pType]
: Enumerable.Empty<Crop>();
}
}
您的代码将变为:
double total = 0
foreach(Crop crop in p.GetCropsByType(t.Type))
total += crop.Area;
return total;
另一种可能更快的可能性是:
internal sealed class P
{
private readonly List<Crop> mCrops = new List<Crop>();
private double mTotalArea;
public IEnumerable<Crop> Crops { get { return mCrops; } }
public double TotalArea { get { return mTotalArea; } }
public void Add(Crop pCrop)
{
mCrops.Add(pCrop);
mTotalArea += pCrop.Area;
}
}
您的代码将只是访问TotalArea属性,您甚至不需要循环:
return p.TotalArea;
您可能还会考虑将管理Crops
数据的代码提取到单独的类中,具体取决于P
。
答案 2 :(得分:1)
这是一个相当直接的总和,所以我怀疑你会看到使用LINQ带来的任何好处。
你还没有告诉我们很多关于这里的设置,但这是一个想法。如果p.Crops
很大且序列中只有少数项目属于所需类型,则可以构建另一个仅包含所需项目的序列。
我假设您在插入p.Crops
时知道类型。如果是这种情况,您可以轻松地将相关项插入另一个集合中,并将其用于sum循环。这将减少N并摆脱比较。它仍然是O(N)。