在可枚举LINQ查询结果用于大型数据集使用ToList() - 效率的问题?(Using ToLis

2019-09-20 03:10发布

我已经做了很多,我目前正在写的应用程序中使用LINQ查询的,而我一直运行到的是具有与LINQ查询结果转换成用于进一步加工列出情形之一的(我有我的理由想要列表)。

我想有一个更好的了解此列表转换的情况下有效率低下,因为我已经反复使用,现在发生了什么。 所以,给我执行一行行是这样的:

var matches = (from x in list1 join y in list2 on x equals y select x).ToList();

问题:

  1. 是否有任何开销这里除了建立一个新的列表,并与在可枚举的元素的引用人口从查询返回?

  2. 你会考虑这个效率低下?

  3. 有没有办法让LINQ查询直接生成一个列表,以避免在这种情况下转换的需要?

Answer 1:

那么,它会创建数据的副本。 这可能是低效的-但要看是怎么回事。 如果你需要一个List<T>末, List<T>通常要接近有效率,你会得到。 一个例外是,如果你打算只是做一个转换和源已经是一个名单-然后使用ConvertAll会更有效率,因为它可以创建大小合适的支持数组开始。

如果你需要到流的数据-例如,你只是做foreach就可以了,并采取不影响原始数据源的行动-然后调用ToList绝对是效率低下的一个潜在来源。 这将迫使整个list1进行评估-如果这是一个惰性计算序列(如“从一个随机数发生器第1,000,000的测量值”),那么这是不好的。 需要注意的是,你正在做一个连接, list2反正只要你尝试拉从序列的第一个值(不管是为了填充列表或不)进行评估。

你可能需要阅读我Edulinq职位上ToList ,看看发生了什么事情-至少在一个可能的实现-在后台运行。



Answer 2:

  1. 没有任何其他overhed除了已经按您mantioned那些人。

  2. 我会说是的,但是这取决于具体的应用场景。 顺便说一句, 一般最好还是避免额外的电话。 (我认为这是明显)。

  3. 恐怕不是。 在LINQ query返回数据序列 ,这可能是一个无穷远序列可能。 转换为List<T>你使它FINIT,与也是索引访问,这是不可能的可能性具有在序列

Suggession:避免在您需要的情况List<T> 如果,顺便说一句,你需要它,里面推的你,你在当前时刻需要更少的数据。

希望这可以帮助。



Answer 3:

除了说了些什么,如果你要加入的最初两个列表已经相当大,创造了三分之一(创建两个“相交”)可能会导致内存不足的错误。 如果你只是迭代LINQ语句的结果,你会大大减少内存使用情况。



Answer 4:

  1. 大部分的开销列表创建类似的连接到数据库之前发生,得到的数据
    适配器,对于VAR类型的.NET需要决定它的数据类型/结构...

  2. 效率是非常相对的概念。 对于一个程序员谁在SQL不强是有效的,更快的发展(相对旧ADO)在1详述的开销。

  3. 在另一方面LINQ可以调用从数据库本身的程序,已经快。 我建议你到下一个测试:

    • 对数据的最大量运行您的程序和测量时间。
    • 使用一些数据库程序,将数据导出到文件(如XML,CSV,....)并尝试从该文件中建立您的清单和测量时间。 然后,你可以看到,如果差异是显著。 但第二个方面是对程序员效率较低,但可减少运行时间。


Answer 5:

Enumerable.ToList(source)本质上只是一个调用new List(source)

此构造将测试源是否是一个ICollection<T>并且如果它是分配适当大小的阵列。 在其他情况下,即,其中所述源是一个LINQ查询大多数情况下,将分配与默认的初始容量(四个项目)的数组,并根据需要通过加倍容量增长它。 每个容量增加一倍的时间,一个新的数组分配和旧的被复制到新的一个。

这可能会引入在案件的一些开销您的列表更是预示了很多的项目(我们可能至少说话千)。 开销能尽快将显著为列表的增长超过85 KB,因为它是那么的大对象堆,这是不板结,可能与内存碎片遭受分配。 请注意,我指的列表中的数组。 如果T是引用类型,该阵列只包含的引用,而不是实际的对象。 这些对象然后不计为85 KB的限制。

你可以删除一些这方面的开销,如果你能准确地估计您的序列的大小(它是更好地高估一点比它低估了一点点)。 例如,如果你只运行.Select()上的东西操作实现ICollection<T>你知道输出列表的大小。

在这种情况下,该扩展方法会减少这方面的开销:

public static List<T> ToList<T>(this IEnumerable<T> source, int initialCapacity)
{
    // parameter validation ommited for brevity

    var result = new List<T>(initialCapacity);

    foreach (T item in source)
    {
        result.Add(item);
    }

    return result;
}

在某些情况下,创建列表只是要取代已经在那里的列表,例如:从以前的运行。 在这种情况下,你可以避免不少的内存分配,如果你重复使用旧名单。 如果你没有到旧列表的并发访问,虽然这只会工作,我也不会做,如果新的名单通常会比旧名单显著小。 如果是这样的话,你可以使用这个扩展方法:

public static void CopyToList<T>(this IEnumerable<T> source, List<T> destination)
{
    // parameter validation ommited for brevity

    destination.Clear();

    foreach (T item in source)
    {
        destination.Add(item);
    }
}

这是说,我会考虑.ToList()是效率低下? 不,如果你有内存,你要重复使用列表中,无论是随机索引到了很多,或者遍历它多次。

现在回到你具体的例子:

var matches = (from x in list1 join y in list2 on x equals y select x).ToList(); 

可能是更有效地做到这一点在一些其他的方式,例如:

var matches = list1.Intersect(list2).ToList();

这将产生相同的结果,如果列表1和List2不包含重复的,而且是非常有效的,如果列表2小。

只有这样,才能真正了解,虽然,像往常一样,是使用典型的工作负载来测量。



文章来源: Using ToList() on Enumerable LINQ query results for large data sets - Efficiency Issue?