提高LINQ性能

时间:2015-12-30 14:31:54

标签: c# .net linq

我有这样的linq声明:

var records = from line in myfile 
              let data = line.Split(',')
              select new { a=int.Parse(data[0]), b=int.Parse(data[1]) };
var average = records.Sum(r => r.b)!=0?records.Sum(r => r.a) / records.Sum(r => r.b):0;

我的问题是: 记录的次数是多少次。(r => r.b)是在最后一行计算的? LINQ是否每次需要计算总和时都会遍历所有记录(在这种情况下,3 Sum()所以循环3次)?或者它是否巧妙地循环遍历所有记录并计算所有总和?

修改1

  1. 我想知道是否有任何方法可以通过全部来改善它 记录只是一次(因为我们只需要在一个循环中完成它) 当使用plain for循环时)?

  2. 并且之前没有必要将所有内容加载到内存中     我们可以做总和和平均。当然,我们可以总结每个元素     从文件中加载它。有没有办法减少记忆     消费也是?

  3. 修改2

    为了澄清一下,在我结束之前我没有使用LINQ。使用plain while / for循环可以实现所有性能要求。但我接着尝试通过使用LINQ来提高可读性并减少代码行。似乎我们无法同时获得两者。

6 个答案:

答案 0 :(得分:9)

两次,像这样写,它会一次:

var sum = records.Sum(r => r.b);

var avarage = sum != 0 ? records.Sum(r => r.a)/sum: 0;

答案 1 :(得分:5)

很多的答案,但没有一个能够解决你的所有问题。

  

在最后一行计算记录数(r => r.b)多少次?

三次。

  

每次需要计算时,LINQ是否会遍历所有记录   一个总和(在这种情况下,3 Sum()所以循环3次)?

  

或者只是巧妙地遍历所有记录一次并计算所有记录   总和?

没有

  

我想知道是否有任何方法可以通过全部来改善它   记录只有一次(因为我们只需要在一个循环中完成它   使用plain for循环)?

你可以这样做,但它需要你急切地加载所有与下一个问题相矛盾的数据。

  

在我们之前,确实没有必要将所有内容加载到内存中   可以做总和和平均。当然,我们可以将每个元素相加   从文件中加载它。有没有办法减少记忆   消费也是?

那是对的。在原始帖子中,您有一个名为myFile的变量,并且您正在迭代它并将其放入名为line的本地变量中(读取:基本上是foreach)。由于您没有显示获取myFile数据的方式,因此我假设您急切地加载了所有数据。

以下是延迟加载数据的简单示例:

public IEnumerable<string> GetData()
{
    using (var fileStream = File.OpenRead(@"C:\Temp\MyData.txt"))
    {
        using (var streamReader = new StreamReader(fileStream))
        {
            string line;
            while ((line = streamReader.ReadLine()) != null)
            {                       
                yield return line;
            }
        }
    }
}

public void CalculateSumAndAverage()
{
    var sumA = 0;
    var sumB = 0;
    var average = 0;

    foreach (var line in GetData())
    {
        var split = line.Split(',');
        var a = Convert.ToInt32(split[0]);
        var b = Convert.ToInt32(split[1]);

        sumA += a;
        sumB += b;
    }

    // I'm not a big fan of ternary operators,
    // but feel free to convert this if you so desire.
    if (sumB != 0)
    {
        average = sumA / sumB;
    }
    else 
    {
        // This else clause is redundant, but I converted it from a ternary operator.
        average = 0;
    }
}

答案 2 :(得分:4)

三次,您应该使用的是return _.sum(_.pluck(dataset, 'cost')) ,而不是Aggregate

Sum

答案 3 :(得分:2)

对Sum方法的每次调用都会遍历myfile中的所有行。 要提高性能,请写:

var records = (from line in myfile 
          let data = line.Split(',')
          select new { a=int.Parse(data[0]), b=int.Parse(data[1]) }).ToList();

所以它会创建包含所有元素的列表(带有“a”和“b”属性),然后每次调用Sum方法都会遍历此列表,而不会拆分和解析数据。 当然,你可以进一步记住一些临时变量中Sum方法的结果。

答案 4 :(得分:1)

詹姆斯,我不是一位专家,这是我的想法。我认为可能会减少到1.也许有更多的代码。记录仍然是AnonymousType {int a,int b}的IEnumerable。

*动态是一种快速解决方法。你应该为它编写一个结构。

int sum_a = 0,sum_b = 0;
Func<string[], dynamic> b = (string[] data) => { 
    sum_a += int.Parse(data[0]); 
    sum_b += int.Parse(data[1]);
    return new {a = int.Parse(data[0]),b = int.Parse(data[0]) }; 
};
var records = from line in fileLines 
              let data = line.Split(',')
              let result = b(data)
              select new { a = (int)result.a, b = (int)result.b };
var average = sum_b != 0 ? sum_a / sum_b : 0;

对于其他结构,这很简单。

public struct Int_Int //May be a class or interface for mapping
{
    public int a = 0, b = 0;        
}

然后

int sum_a = 0,sum_b = 0;    
Func<string[], Int_Int> b = (string[] data) => { 
    sum_a += int.Parse(data[0]); 
    sum_b += int.Parse(data[1]);
    return new Int_Int() { a = int.Parse(data[0]), b = int.Parse(data[0]) }; 
};
var records = from line in fileLines
              let data = line.Split(',')
              select b(data);
var average = sum_b != 0 ? sum_a / sum_b : 0;

答案 5 :(得分:0)

SUM会在您调用它时获取所有记录,我建议您使用ToList() - &gt; Do you ToList()?

var records = from line in myfile 
              let data = line.Split(',')
              select new { a=int.Parse(data[0]), b=int.Parse(data[1]) }.ToList();

var sumb = records.Sum(r => r.b);
var average = sumb !=0?records.Sum(r => r.a) / sumb :0;