测试一下C# ArryList与List的性能差异,结果有点意外
作者:admin 时间:2023-5-13 7:32:26 浏览:前面介绍过官方建议使用List而不是ArryList,虽然理论上那样说,但没有经过实验测试比较,不太能令人信服。出于好奇心,便测试了一下C# ArryList
与List
的性能差异,结果有点意外。
前几天,一位朋友问了我一个简单的问题,我看了看他的代码,他没有使用 List<T>
,而是使用了类型“ArrayList
”。老实说,我甚至不记得上次使用 ArrayList
是什么时候了。我认为当我开始在 .NET 2 中编程时,我可能无法足够快地理解泛型,而 ArrayList
似乎是一个替代品。
有什么不同?
两者之间的主要区别在于 ArrayList
仅包含“对象”类型,这意味着理论上它是一盒你想要的任何东西,例如这段代码编译得很好:
ArrayList arrayList = new ArrayList();
arrayList.Add(123);
arrayList.Add("abc");
arrayList.Add(new object());
然后在代码上从数组列表中抓取内容以“检查”它的类型是否正确。在实践中,你不会随意地将各种类型放入数组列表中,因此实际上它更像是编译时的“松散”。如果我们将它与 List
进行比较:
List<int> list = new List<int>();
list.Add(123);
list.Add("abc"); //编译时错误
它知道我们只想存储整数并且试图将任何其他东西塞进去是行不通的。
但是这个呢?
List<object> list = new List<object>();
list.Add(123);
list.Add("abc");
这行得通吗?对象列表几乎与 ArrayList
相同。
如果我们查看 ArrayList
实现的接口:
public class ArrayList : ICollection, IEnumerable, IList, ICloneable
List
基本上与一些通用接口相同,但是当你检查这些时,除了使用类型的“Add
”等通用方法之外,它们不会添加任何东西:
public class List<T> : ICollection<T>, IEnumerable<T>, IEnumerable, IList<T>, IReadOnlyCollection<T>, IReadOnlyList<T>, ICollection, IList
但事情发生变化的地方是使用 LINQ
。几乎所有方法(我说几乎,但我认为它是全部)都建立在 IEnumerable<T>
而不是 IEnumerable
之上。例如,LINQ
中的 Where
子句如下所示:
public static IEnumerable<TSource> Where<TSource>(this IEnumerable<TSource> source, Func<TSource, bool> predicate)
这意味着你不能在 ArrayList
上使用 LINQ
。在某些用例中,这没什么大不了的,但你确实可以免费获得 LINQ
……所以选择一个不支持它的类型真的是无缘无故地搬起石头砸自己的脚。
性能影响
在某些情况下,在选择 ArrayList
而不是 List<T>
时会对性能产生很大影响。这归结为“装箱”和“拆箱”的行为。简单来说,装箱就是采用一个值类型(比如整数)并将其包装在一个对象中,并将其存储在堆上而不是堆栈上。
但这对 List
与 ArrayList
有何影响?当我们在 ArrayList
中存储一个项目时,它必须是对象类型(或类型),如果我们在 ArrayList
中存储一个值类型,那么在存储它之前,它必须先“装箱”对象并将其包装起来,List<int>
没有相同的装箱成本(尽管 List<object>
会)。
为了对此进行测试,我使用BenchmarkDotNet创建了一个基准。BenchmarkDotNet可帮助你将方法转化为基准、跟踪其性能并共享可重现的测量实验。参阅文章:
public class ArrayListVsListWrite
{
int itemCount = 10000000;
public ArrayList arrayList;
public List<int> list;
public List<object> listObject;
[IterationSetup]
public void Setup()
{
arrayList = new ArrayList();
list = new List<int>();
listObject = new List<object>();
}
[Benchmark]
public ArrayList WriteArrayList()
{
for(int i=0; i < itemCount; i++)
{
arrayList.Add(i);
}
return arrayList;
}
[Benchmark]
public List<object> WriteListObject()
{
for (int i = 0; i < itemCount; i++)
{
listObject.Add(i);
}
return listObject;
}
[Benchmark]
public List<int> WriteList()
{
for (int i = 0; i < itemCount; i++)
{
list.Add(i);
}
return list;
}
}
简单来说,我们正在循环 1000 万次并将项目添加到列表中。结果是:
方法 | 平均 | 错误 | 标准偏差 |
---|---|---|---|
WriteArrayList | 651.48 ms | 4.215 ms | 3.943 ms |
WriteListObject | 641.95 ms | 5.129 ms | 4.798 ms |
WriteList | 88.49 ms | 5.631 ms | 16.603 ms |
不难看出这里的性能差异。我们可以看到 List
类型的对象也存在同样的装箱/拆箱问题。
在较小程度上,读取也会变慢,因为你将读取一个对象,然后“拆箱”该对象并将其转换为整数。
什么时候应该使用 ArrayList?
绝不。
唯一应该使用 ArrayList
的时候是使用在 List<T>
之前构建的库时,我相信它是在 .NET 2.0 中引入的。如果你使用的是面向 .NET 1.0 或 1.1 的库(或构建代码),那么我猜 ArrayList
没问题。
相关文章
- 站长推荐