我测试了List<string>
vs IEnumerable<string>
使用for
和foreach
循环进行迭代,List可能更快吗?
这些是我能找到的少数链接中的两个,这些链接公开声明性能更好地迭代IEnumerable
而不是List
。
我的测试是从包含网址列表的文本文件中加载10K行。
我先将它加载到List中,然后将List复制到IEnumerable
List<string> StrByLst = ...method to load records from the file .
IEnumerable StrsByIE = StrByLst;
所以每个人都有10k个项目<string>
在每个集合上循环100次,意味着100K迭代,结果为
List<string>
比IEnumerable<string>
是可以预测的吗?
这是进行测试的代码
string WorkDirtPath = HostingEnvironment.ApplicationPhysicalPath;
string fileName = "tst.txt";
string fileToLoad = Path.Combine(WorkDirtPath, fileName);
List<string> ListfromStream = new List<string>();
ListfromStream = PopulateListStrwithAnyFile(fileToLoad) ;
IEnumerable<string> IEnumFromStream = ListfromStream ;
string trslt = "";
Stopwatch SwFr = new Stopwatch();
Stopwatch SwFe = new Stopwatch();
string resultFrLst = "",resultFrIEnumrable, resultFe = "", Container = "";
SwFr.Start();
for (int itr = 0; itr < 100; itr++)
{
for (int i = 0; i < ListfromStream.Count(); i++)
{
Container = ListfromStream.ElementAt(i);
}
//the stop() was here , i was doing changes , so my mistake.
}
SwFr.Stop();
resultFrLst = SwFr.Elapsed.ToString();
//forgot to do this reset though still it is faster (x56??)
SwFr.Reset();
SwFr.Start();
for(int itr = 0; itr<100; itr++)
{
for (int i = 0; i < IEnumFromStream.Count(); i++)
{
Container = IEnumFromStream.ElementAt(i);
}
}
SwFr.Stop();
resultFrIEnumrable = SwFr.Elapsed.ToString();
更新...最终
将计数器取出到for循环之外,
IEnumerable&amp;的 int counter = ..count
列表
然后传递counter(int)作为@ScottChamberlain建议的总项数。 重新检查每件事情是否到位,现在结果是IEnumerable快了5%。 总结,使用情景 - 用例...根本没有性能差异......
答案 0 :(得分:4)
你做错了什么。
你得到的时间应该非常接近,因为你运行的代码基本相同。
IEnumerable只是一个List实现的接口,所以当你在IEnumerable引用上调用一些方法时,它最终会调用List的相应方法。
IEnumerable中没有实现代码 - 这就是接口 - 它们只指定一个类应具有的功能,但不说明它是如何实现的。
答案 1 :(得分:3)
您的测试有一些问题,一个是IEnumFromStream.Count()
循环内的for
,每次想要获取该值时,它必须在整个列表中枚举以获取计数和值不会在循环之间缓存。将该调用移到for
循环之外并将结果保存在int
中,并将该值用于for
循环,您将看到IEnumerable的更短时间。
IEnumFromStream.ElementAt(i)
的行为与Count()
类似,它必须在整个列表中迭代到i
(例如:第一次0
,第二次{{1}每次0,1
可以直接跳转到它需要的索引时,},第三个0,1,2
等等...)您应该使用IEnumerator
返回的GetEnumerator()
代替。
List
和IEnumerable
循环不能很好地混合。使用正确的工具进行工作,请致电GetEnumerator()
并使用该工具或在for
循环中使用它。
现在,我知道很多人可能会说“但这是一个界面,它只是映射调用,它应该没有区别”,但有一个关键的事情,foreach
没有IEnumerable<T>
或Count()
方法! 。这些方法是LINQ添加的扩展方法,LINQ类不知道底层集合是List,因此它知道底层对象可以做什么,并且每次调用该方法时都会遍历列表。 / p>
ElementAt()
使用IEnumerable
IEnumerator
上面的代码与
基本相同using(var enu = IEnumFromStream.GetEnumerator())
{
//You have to call "MoveNext()" once before getting "Current" the first time,
// this is done so you can have a nice clean while loop like this.
while(enu.MoveNext())
{
Container = enu.Current;
}
}
要记住的重要一点是foreach(var enu in IEnumFromStream)
{
Container = enu;
}
没有长度,实际上它们可以无限长。有a whole field计算机科学检测无限长的IEnumerable
答案 2 :(得分:1)
根据您发布的代码,我认为问题在于您使用Stopwatch
类。
您声明其中两个,SwFr
和SwFe
,但仅使用前者。因此,对SwFr.Elapsed
的最后一次调用将获得两个 for
个循环集合的总时间。
如果您想以这种方式重复使用该对象,请在SwFr.Reset()
之后立即拨打resultFrLst = SwFr.Elapsed.ToString();
。
或者,您可以在运行第二次测试时使用SwFe
。