关于for与foreach的争论,一直都存在。不过有些人只是拿array来测试,有些人只是拿arraylist测试,有些拿valuetype测试,有些拿引用类型测试,测试结果大相径庭,让我不知道相信哪个。于是今天自己测试了一下。
我没有做很全面的测试,只选择了其中四种,而且四种都是针对引用类型来测试的,而没有针对值类型测试。四种测试方法如下:
1、使用Array,也就是object[],比较for与foreach循环的耗时。
2、使用Array,但存放自定义类型,比较for与foreach循环的耗时。
3、使用ArrayList,存放简单的object类型,比较for与foreach循环的耗时。
4、使用ArrayList,存放自定义类型,比较for与foreach循环的耗时。
注1:我没有使用值类型来测试。foreach实际上是转换为了对枚举器的调用,而枚举器返回类型是object类型,所以值类型的数组在每次foreach访问的时候都会有一次装箱和一次拆箱的操作,这个会很大程度的影响遍历的性能。因此对值类型数组的遍历for的性能更好是有可能的。
注2:我使用的自定义类型的定义如下:
public class EntityBase : DictionaryBase, System.Runtime.Serialization.ISerializable, System.Runtime.Serialization.IDeserializationCallback
注3:我没有测试自定义的强类型集合或者其他实现了IList接口的类型比如DataTable,因为我觉得具体情况要取决于枚举器的实现,而实际上我们的强类型集合在继承了IList或者CollectionBase之后一般不会实现自定义的枚举器,即使实现了也是在.net的枚举器基础上做了表面上的修改而不会修改其核心算法,因此结果应该与ArrayList的测试结果一样。
注4:Dll以及Console都是在Debug模式测试的。
具体测试代码如下:
Dll中的测试方法:
public class Test
...{
public void ArrayTest()
...{
Console.WriteLine("ArrayTest");
// 初始化工作
object[] arr = new object[100000];
for(int index = 0; index < 100000; index ++)
...{
arr[index] = new object();
}
string str;
// 开始两种不同的循环
DateTime start = DateTime.Now;
for(int index = 0; index < arr.Length; index ++)
...{
str = arr[index].ToString();
}
DateTime end = DateTime.Now;
foreach(object obj in arr)
...{
str = obj.ToString();
}
DateTime end2 = DateTime.Now;
TimeSpan span = end - start;
Console.WriteLine("for: " + span.TotalMilliseconds.ToString());
span = end2 - end;
Console.WriteLine("foreach: " + span.TotalMilliseconds);
}
public void MyObjectArrayTest()
...{
Console.WriteLine("MyObjectArrayTest");
// 初始化工作
object[] arr = new object[100000];
for(int index = 0; index < 100000; index ++)
...{
arr[index] = new EntityBase();
}
string str;
// 开始两种不同的循环
DateTime start = DateTime.Now;
for(int index = 0; index < arr.Length; index ++)
...{
str = ((EntityBase)arr[index]).WriteXml(Formatting.Indented);
}
DateTime end = DateTime.Now;
// c# 有两种类型转换,foreach 语句中会自动执行 as 类型转换,为了尽量避免这种不一样的地方、
// 与 for 语句中执行同样的类型转换,没有使用 foreach(EntityBase obj in arr) 。
foreach(object obj in arr)
...{
str = ((EntityBase)obj).WriteXml(Formatting.Indented);
}
DateTime end2 = DateTime.Now;
TimeSpan span = end - start;
Console.WriteLine("for: " + span.TotalMilliseconds.ToString());
span = end2 - end;
Console.WriteLine("foreach: " + span.TotalMilliseconds);
}
public void SimpleObjectListTest()
...{
Console.WriteLine("SimpleObjectListTest");
// 初始化工作
ArrayList arr = new ArrayList();
for(int index = 0; index < 100000; index ++)
...{
arr.Add(new object());
}
string str;
// 开始两种不同的循环
DateTime start = DateTime.Now;
for(int index = 0; index < arr.Count; index ++)
...{
str = arr[index].ToString();
}
DateTime end = DateTime.Now;
foreach(object obj in arr)
...{
str = obj.ToString();
}
DateTime end2 = DateTime.Now;
TimeSpan span = end - start;
Console.WriteLine("for: " + span.TotalMilliseconds.ToString());
span = end2 - end;
Console.WriteLine("foreach: " + span.TotalMilliseconds);
}
public void MyObjectListTest()
...{
Console.WriteLine("MyObjectListTest");
// 初始化工作
ArrayList arr = new ArrayList();
for(int index = 0; index < 100000; index ++)
...{
arr.Add(new EntityBase());
}
string str;
// 开始两种不同的循环
DateTime start = DateTime.Now;
for(int index = 0; index < arr.Count; index ++)
...{
str = ((EntityBase)arr[index]).WriteXml(Formatting.Indented);
}
DateTime end = DateTime.Now;
// c# 有两种类型转换,foreach 语句中会自动执行 as 类型转换,为了尽量避免这种不一样的地方、
// 与 for 语句中执行同样的类型转换,没有使用 foreach(EntityBase obj in arr) 。
foreach(object obj in arr)
...{
str = ((EntityBase)obj).WriteXml(Formatting.Indented);
}
DateTime end2 = DateTime.Now;
TimeSpan span = end - start;
Console.WriteLine("for: " + span.TotalMilliseconds.ToString());
span = end2 - end;
Console.WriteLine("foreach: " + span.TotalMilliseconds);
}
}
Console控制台的调用:
class Class1
...{
Main#region Main
[STAThread]
static void Main(string[] args)
...{
bool flag = true;
while(flag)
...{
Console.WriteLine("please input command:");
string cmd = Console.ReadLine().Trim();
while(cmd.IndexOf(" ") != -1)
...{
cmd = cmd.Replace(" ", " ");
}
switch(cmd)
...{
case "":
continue;
case "exit":
case "quit":
flag = false;
break;
default:
try
...{
new Class1().DoMain(cmd, args);
}
catch(Exception ex)
...{
Console.WriteLine(ExceptionHandler.XmlFormatException(ex));
}
break;
}
}
Console.WriteLine("will exit.");
}
#endregion
public void DoMain(string cmd, string[] args)
...{
TestDll.Test test = new TestDll.Test();
test.ArrayTest();
test.MyObjectArrayTest();
test.SimpleObjectListTest();
test.MyObjectListTest();
}
}
为了去掉其他如.net机制等造成的影响,编译完成后,我通过cmd.exe先执行了几遍调用,然后开始真正的测试。
最终输出如下:
please input command:
test
ArrayTest
for: 15.625
foreach: 15.625
MyObjectArrayTest
for: 2250
foreach: 812.5
SimpleObjectListTest
for: 15.625
foreach: 31.25
MyObjectListTest
for: 2312.5
foreach: 859.375
please input command:
exit
will exit.
// 注:上面的调用是为了消除.net机制等引起的第一次执行可能引起的额外影响。下面真正开始测试。
E:/My Projects/ConsoleTester/bin/Debug>ConsoleTester.exe
please input command:
test
ArrayTest
for: 15.625
foreach: 15.625
MyObjectArrayTest
for: 2046.875
foreach: 812.5
SimpleObjectListTest
for: 31.25
foreach: 15.625
MyObjectListTest
for: 2187.5
foreach: 890.625
please input command:
test
ArrayTest
for: 31.25
foreach: 15.625
MyObjectArrayTest
for: 2187.5
foreach: 843.75
SimpleObjectListTest
for: 31.25
foreach: 15.625
MyObjectListTest
for: 2203.125
foreach: 859.375
please input command:
test
ArrayTest
for: 15.625
foreach: 15.625
MyObjectArrayTest
for: 2312.5
foreach: 812.5
SimpleObjectListTest
for: 31.25
foreach: 15.625
MyObjectListTest
for: 2375
foreach: 843.75
please input command:
test
ArrayTest
for: 15.625
foreach: 15.625
MyObjectArrayTest
for: 2359.375
foreach: 828.125
SimpleObjectListTest
for: 15.625
foreach: 15.625
MyObjectListTest
for: 2343.75
foreach: 843.75
please input command:
test
ArrayTest
for: 15.625
foreach: 31.25
MyObjectArrayTest
for: 2218.75
foreach: 812.5
SimpleObjectListTest
for: 15.625
foreach: 46.875
MyObjectListTest
for: 2359.375
foreach: 859.375
please input command:
test
ArrayTest
for: 15.625
foreach: 31.25
MyObjectArrayTest
for: 2171.875
foreach: 843.75
SimpleObjectListTest
for: 31.25
foreach: 15.625
MyObjectListTest
for: 2375
foreach: 859.375
please input command:
test
ArrayTest
for: 15.625
foreach: 31.25
MyObjectArrayTest
for: 2203.125
foreach: 812.5
SimpleObjectListTest
for: 31.25
foreach: 15.625
MyObjectListTest
for: 2343.75
foreach: 875
please input command:
test
ArrayTest
for: 15.625
foreach: 15.625
MyObjectArrayTest
for: 2250
foreach: 812.5
SimpleObjectListTest
for: 15.625
foreach: 31.25
MyObjectListTest
for: 2312.5
foreach: 859.375
please input command:
test
ArrayTest
for: 15.625
foreach: 15.625
MyObjectArrayTest
for: 2265.625
foreach: 828.125
SimpleObjectListTest
for: 15.625
foreach: 31.25
MyObjectListTest
for: 2343.75
foreach: 859.375
please input command:
test
ArrayTest
for: 15.625
foreach: 15.625
MyObjectArrayTest
for: 2250
foreach: 843.75
SimpleObjectListTest
for: 15.625
foreach: 31.25
MyObjectListTest
for: 2406.25
foreach: 843.75
please input command:
从上面的结果来看,测试结果有些动荡,不过我们通过大多数情况,就可以得到一些一般性结论。具体结论我也就不说了。虽然测试的覆盖面并不是很全,不过个人觉得至少代表了我们实际应用中的大部分情况。
测试完毕。
顺便贴下Dll反编译的结果:(有人说反编译发现foreach都变成了for,事实好像不是这样的)
public void ArrayTest()
...{
string text1;
Console.WriteLine("ArrayTest");
object[] objArray1 = new object[0x186a0];
for (int num1 = 0; num1 < 0x186a0; num1++)
...{
objArray1[num1] = new object();
}
DateTime time1 = DateTime.Now;
for (int num2 = 0; num2 < objArray1.Length; num2++)
...{
text1 = objArray1[num2].ToString();
}
DateTime time2 = DateTime.Now;
foreach (object obj1 in objArray1)
...{
text1 = obj1.ToString();
}
DateTime time3 = DateTime.Now;
TimeSpan span1 = (TimeSpan) (time2 - time1);
Console.WriteLine("for: " + span1.TotalMilliseconds.ToString());
span1 = (TimeSpan) (time3 - time2);
Console.WriteLine("foreach: " + span1.TotalMilliseconds);
}

public void MyObjectArrayTest()
...{
string text1;
Console.WriteLine("MyObjectArrayTest");
object[] objArray1 = new object[0x186a0];
for (int num1 = 0; num1 < 0x186a0; num1++)
...{
objArray1[num1] = new EntityBase();
}
DateTime time1 = DateTime.Now;
for (int num2 = 0; num2 < objArray1.Length; num2++)
...{
text1 = ((EntityBase) objArray1[num2]).WriteXml(Formatting.Indented);
}
DateTime time2 = DateTime.Now;
foreach (object obj1 in objArray1)
...{
text1 = ((EntityBase) obj1).WriteXml(Formatting.Indented);
}
DateTime time3 = DateTime.Now;
TimeSpan span1 = (TimeSpan) (time2 - time1);
Console.WriteLine("for: " + span1.TotalMilliseconds.ToString());
span1 = (TimeSpan) (time3 - time2);
Console.WriteLine("foreach: " + span1.TotalMilliseconds);
}
public void MyObjectListTest()
...{
string text1;
Console.WriteLine("MyObjectListTest");
ArrayList list1 = new ArrayList();
for (int num1 = 0; num1 < 0x186a0; num1++)
...{
list1.Add(new EntityBase());
}
DateTime time1 = DateTime.Now;
for (int num2 = 0; num2 < list1.Count; num2++)
...{
text1 = ((EntityBase) list1[num2]).WriteXml(Formatting.Indented);
}
DateTime time2 = DateTime.Now;
foreach (object obj1 in list1)
...{
text1 = ((EntityBase) obj1).WriteXml(Formatting.Indented);
}
DateTime time3 = DateTime.Now;
TimeSpan span1 = (TimeSpan) (time2 - time1);
Console.WriteLine("for: " + span1.TotalMilliseconds.ToString());
span1 = (TimeSpan) (time3 - time2);
Console.WriteLine("foreach: " + span1.TotalMilliseconds);
}
public void SimpleObjectListTest()
...{
string text1;
Console.WriteLine("SimpleObjectListTest");
ArrayList list1 = new ArrayList();
for (int num1 = 0; num1 < 0x186a0; num1++)
...{
list1.Add(new object());
}
DateTime time1 = DateTime.Now;
for (int num2 = 0; num2 < list1.Count; num2++)
...{
text1 = list1[num2].ToString();
}
DateTime time2 = DateTime.Now;
foreach (object obj1 in list1)
...{
text1 = obj1.ToString();
}
DateTime time3 = DateTime.Now;
TimeSpan span1 = (TimeSpan) (time2 - time1);
Console.WriteLine("for: " + span1.TotalMilliseconds.ToString());
span1 = (TimeSpan) (time3 - time2);
Console.WriteLine("foreach: " + span1.TotalMilliseconds);
}

本文通过四个测试场景对比了for循环与foreach循环在不同情况下的性能表现,包括使用Array和ArrayList存储简单对象类型及自定义类型的情况。
287

被折叠的 条评论
为什么被折叠?



