我的问题非常简单。我什么时候应该使用List,IEnumerable和ArrayList。
这是我的情景。我正在使用LINQ在Web应用程序中工作。信息以IEnumerable:
的形式返回IEnumerable<Inventory> result = from Inventory i in db where....
我不确定IEnumerable是如何工作的,但是每个操作都需要花费很多时间来执行。更具体地说,result.Count(),result.ElementAt(i),result.ToList等,每个操作都需要相当长的时间。
所以,我想知道我是否应该通过执行result.ToList将其视为List,而不是使用IEnumerable变量。
谢谢!
答案 0 :(得分:6)
如果我理解你正在做什么,你会有from Inventory i in db select i
这样的查询,然后对结果做几个操作:
var count = result.Count();
var fifth = result.ElementAt(5);
var allItems = result.ToList();
现在考虑将查询作为不同类型时会发生什么:
IQueryable<T>
var result = from Inventory i in db select i;
IQueryable<Inventory> result = from Inventory i in db select i;
以上两行是相同的。它们实际上并没有进入数据库,只是创建了查询的表示。如果你有这个,Count()
将执行像SELECT COUNT(*) FROM Inventory
这样的SQL查询,ElementAt(5)
将执行另一个只接受表中第五项的查询,而ToList()
将执行类似SELECT * FROM Inventory
,但这就是我们想要的。
IEnumerable<T>
IEnumerable<Inventory> result = from Inventory i in db select i;
再次执行此操作不会进入数据库,它只会创建查询的表示形式。但它是一种不能使用特定于IQueryable<T>
的方法的表示,因此任何LINQ操作都将枚举该集合,该集合将执行SELECT * FROM Inventory
之类的SQL查询。
因此,对于示例:Count()
将仅执行SELECT * …
查询以计算结果中的项目。 ElementAt(5)
将再次执行整个查询 ,只会丢弃除第五个以外的所有项目。 ToList()
将再次执行查询 。
List<T>
List<Inventory> result = (from Inventory i in db select i).ToList();
这实际上会立即执行SELECT * FROM Inventory
查询,一次。您使用result
执行的所有操作都不会触及数据库,它们将在内存中完成。
你应该从中拿走什么?首先,从不使用IEnumerable<T>
作为数据库查询的类型。它表现糟糕。
如果您想对结果进行多项不同的操作,使用IQueryable<T>
可能是最佳解决方案。
如果您想要检索整个结果,请尽快使用ToList()
(或ToArray()
),然后使用生成的List<T>
。
答案 1 :(得分:4)
永远不要使用ArrayList。保留ArrayList以与pre -.NET 2.0兼容。它相当于List<object>
,并且没有理由不在任何正常情况下使用泛型类型。
从您的代码示例中可以看出,您正在使用LINQ to SQL或类似的框架从数据库中获取数据。在这种情况下,select
语句本身不会带来数据,它只是构造查询。当你调用像Count()或ToList()这样的方法时,它会获取数据 - 这就是它看起来很慢的原因。它并不慢,只是在行动中的延迟加载。
使用IEnumerable的优点是您不必一次加载所有数据。如果您只是使用特定的where
子句进行查询,或者调用Take(1)来获取第一个元素,那么LINQ提供程序应该足够智能,只能从数据库中获取必要的元素。但是如果你调用Count()或ToList(),它必须检索整个数据集。如果您发现自己需要这类信息,您可能需要致电ToList
或ToArray
并在内存列表中完成剩余的工作,这样您就不必点击了DB再次。
答案 2 :(得分:2)
只有在调用ToList()或其他类似方法时才会执行查询。
只要result
可以使用IEnumerable。执行的性能LINQ不依赖于你对result
的使用,因为最终它被视为IEnumerable。
但LINQ性能取决于基础数据。
[WAS EDITED WITH DETAILS]
答案 3 :(得分:1)
使用IEnumerable或IList之间的区别实际上非常简单(表面上)。
您应该查看两个接口定义的合同。 IEnumerable只允许您枚举序列。换句话说,访问数据的唯一方法是使用枚举器,通常在foreach循环中。所以count函数的简单实现就像:
public static int Count(this IEnumerable<T> source) {
int count = 0;
foreach(var item in myEnumerable)
{
count++;
}
return count;
}
这意味着计算可枚举项目数量所需的时间将随项目数量线性增加。此外,因为内部没有以任何方式存储,所以每次想要计数时都必须执行此循环。
IList已经公开了Count属性。这是合同的一部分。要实现Count(),你只需要调用Count属性。无论项目数量如何,这都将花费相同的时间。
考虑这一点的一个简单方法是(特别是使用Linq)将IEnumerable视为您需要的项目的规范。只要您不访问数据,几乎不需要任何时间来构建。一旦你开始枚举(任何返回基本上不是IEnumerable的东西),代码就会执行,可能需要一些时间。
至于你的上下文,我通常喜欢做的是将Linq执行保留在控制器中。所以我进行构建,然后在将其发送到视图之前ToList或ToArray。原因很简单:如果我必须做的只是简单地访问视图中的数据,这意味着我在我看来做的太多了。我现在被迫将这个逻辑移动到我的控制器动作中,使我的视图尽可能干净。
答案 4 :(得分:0)
如果对Linq查询提供程序使用linq表达式,则结果将是IQueryable<T>
,这是IEnumerable<T>
的扩展名。
每次迭代IQueryable<T>
时,Linq查询提供程序将对基础数据源执行查询。因此,如果您希望多次迭代结果,则首先将其转换为列表会更有效(.ToList()
)。
请注意,将结果转换为列表时,应使用List<T>
的实际成员,而不是IEnumerable<T>
的扩展方法。例如,list.ElementAt(i)
和list.Count()
都在O(n)
时间内执行,而list[i]
和list.Count
则会在固定时间内执行。
答案 5 :(得分:0)
尽可能使用通用列表/ IEnumerable。
避免使用ArrayList
。这可能导致值类型的装箱和参考类型的转换。 IEnumerable
是相同的 - 除非你处理对象,否则最好避免使用。
IEnumerable<T>
表现出非常好的协方差,逆变特征。然而它显示delayed execution
这是一个诅咒和祝福。
List<T>
时, IEnumerable<T>
更适合内部使用。 List<T>
不支持逆变。
答案 6 :(得分:0)
使用的答案是“它取决于,但主要是使用List”。
根据问题的全部内容(长时间延迟运行.Count()和其他方法),您应首先对查询结果执行toList(),然后将其用于任何进一步的访问。
这就是原因。 IEnumerable几乎是一个查询。由于被查询的数据可以在查询的运行之间发生变化,因此对该IEnumerable的单个方法调用会导致另一个数据库查找。
因此,每次调用.Count()时,都必须访问数据库并获取与查询匹配的所有对象的计数。每次执行elementAt(x)时,即使x没有改变,有人仍然需要通过数据库并获取其中的任何内容,因为IEnumerable不能假设数据没有改变。
另一方面,如果您使用List获得了查询的快照,那么获取Count或访问随机元素的速度非常快。
那么,使用哪种 - 取决于它。如果每次访问IEnumerable时,您需要知道数据库(或任何数据源)中的内容,那么您必须使用IEnumerable。如果您只关心执行初始查询时的内容或需要对一致(和/或静态)数据源执行操作,请使用List。你第一次访问时仍然会花些时间,但其他一切都会很快。