C#中的ArrayList与List<>有何区别?

497

ArrayListList<>在C#中有什么区别?

仅仅是因为List<>有一个类型而ArrayList没有吗?


7
可能是 ArrayList vs List<object> 的重复问题。 - John Saunders
9
这是一个棘手的问题,但我认为并不完全重复。这个问题询问 List<> 的一般情况,而那个问题则专门询问了 List<object> - goodeye
发现了这个非常有用的博客,可能会有所帮助。我想分享链接:http://fintechexplained.blogspot.co.uk/2017/07/data-structures-comparison.html - InfoLearner
12个回答

635
是的,基本上是这样。List<T> 是一种通用类。它支持存储特定类型的值,无需在 objectT 是值类型时进行装箱/拆箱(在 ArrayList 的情况下会产生开销) 。ArrayList 仅存储 object 引用。作为一种通用集合,List<T> 实现了通用的 IEnumerable<T> 接口,并且可以轻松地在 LINQ 中使用(无需调用任何 CastOfType)。 ArrayList 属于 C# 没有泛型的时代。它已过时,推荐使用 List<T>。除非必须与使用它的旧 API 进行接口交互,否则不应在面向 .NET >= 2.0 的新代码中使用 ArrayList

您介意解释一下为什么使用了“装箱”而不是“强制类型转换”吗?这里发生了什么样的装箱操作?对象是否被分配/释放? - Benjamin Gruenbaum
3
您说得对,使用类型强制转换可能更通用一些。不过,当您处理值类型时(我在写“装箱”时就是这个假设),运行时的真正区别在于此。对于引用类型,其行为在运行时实际上与ArrayList相同。但在静态方面,在使用ArrayList时需要进行类型转换。 - Mehrdad Afshari
我在想框架是否应该限制T为“object”类型,因为ArrayList隐式允许这样做。 - rajibdotnet
我想(迟来地)补充@ScottAdams的观点:该博客谈到了Java 5泛型实现的问题,这与.NET的实现有足够大的差异,以至于它与这个问题无关。帖子中提到的两个“有害”示例在.NET中都没有问题,因此如果您想讨论.NET泛型的“陷阱”,则需要使用其他来源。 - David Schwartz
装箱和拆箱是指类型转换(装箱为隐式转换,拆箱则为显式转换)。 当一个值被装箱后,它会被存储在堆上并且占用更多内存。 拆箱一个值需要更多时间。 通常情况下应尽可能避免使用装箱和拆箱操作。 您可以在此处了解更多信息:https://learn.microsoft.com/en-us/dotnet/csharp/programming-guide/types/boxing-and-unboxing - NeoH4x0r

123

使用 List<T> 可以避免强制类型转换错误,这非常有用,可避免在运行时出现类型转换错误。

示例:

这里(使用 ArrayList),你可以编译此代码,但稍后将会看到执行错误。

ArrayList array1 = new ArrayList();
array1.Add(1);
array1.Add("Pony"); //No error at compile process
int total = 0;
foreach (int num in array1)
{
 total += num; //-->Runtime Error
}
如果您使用List,则可以避免这些错误:
List<int> list1 = new List<int>();
list1.Add(1);
//list1.Add("Pony"); //<-- Error at compile process
int total = 0;
foreach (int num in list1 )
{
 total += num;
}

参考: MSDN


您可以在从ArrayList中提取数据时检查类型,以避免强制转换错误。现在人们使用对象,使ArrayList不再必要。 - user1853517
2
将 i +1 添加到调整中,但您仍然可以在数组列表中使用 if(num is int){} 来避免错误。 - Mina Gabriel
1
避免类型转换错误和装箱开销。这基本上是泛型的一般原因。 - marsze

34

除了上述的几点之外,使用 ArrayList 在 64 位操作系统中所占用的内存是使用 32 位操作系统时的两倍。与此同时,泛型列表 List<T> 所占用的内存要比 ArrayList 少得多。

例如,如果我们在 32 位操作系统中使用一个 19MB 的 ArrayList,那么在 64 位操作系统中就需要占用 39MB 的内存。但是如果你有一个 8MB 的泛型列表 List<int> 在 32 位操作系统中,那么在 64 位操作系统中只需要占用 8.1MB 的内存,这是一个高达 481% 的巨大差异,相较于 ArrayList 来说。

来源:ArrayList’s vs. generic List for primitive types and 64-bits


8
这仅适用于存储值类型,而不适用于引用类型。原因在于ArrayList只能包含指针,并且数据本身需要存储在其他位置。另一方面,值类型可以直接存储在列表中。 - Rasmus Damgaard Nielsen

22
另一个需要补充的区别是关于线程同步。

ArrayList 通过 Synchronized 属性提供了一些线程安全性,它返回一个围绕该集合的线程安全包装器。这个包装器通过在每个添加或删除操作上锁定整个集合来工作。因此,试图访问集合的每个线程必须等待轮到它获取一个锁。这样做不可扩展,并且对于大型集合可能会导致显着的性能降低。

List<T> 不提供任何线程同步;当多个线程同时添加或删除项目时,用户代码必须提供所有同步。

更多信息请参见.Net Framework中的线程同步


我并不是说你应该使用ArrayList,如果可以避免的话,但这个理由很愚蠢。毕竟,包装器是完全可选的;如果你不需要锁定或者需要更细粒度的控制,就不要使用包装器。 - Thorarin
1
如果您想要线程安全,请在考虑 ArrayList 之前查看 System.Collections.Concurrent 命名空间。 - Ykok

21

简单来说,

ArrayList是非泛型的

  • 它是一个对象类型,因此您可以将任何数据类型存储到其中。
  • 您可以存储任何值(值类型或引用类型),例如字符串、整数、员工和对象在ArrayList中。(注意and)
  • 会发生装箱和拆箱。
  • 不安全的类型。
  • 这是旧的。

List是泛型的

  • 它是一种类型的类型,因此您可以在运行时指定T。
  • 基于声明,您只能存储类型T的一个值(字符串或整数或员工或对象)。 (注意or)
  • 不会发生装箱和拆箱。
  • 类型安全。
  • 这是新的。

例子:

ArrayList arrayList = new ArrayList();
List<int> list = new List<int>();

arrayList.Add(1);
arrayList.Add("String");
arrayList.Add(new object());


list.Add(1);
list.Add("String");                 // Compile-time Error
list.Add(new object());             // Compile-time Error

请阅读微软官方文档: https://blogs.msdn.microsoft.com/kcwalina/2005/09/23/system-collections-vs-system-collection-generic-and-system-collections-objectmodel/

enter image description here

注意: 在理解区别之前,您应该了解泛型: https://learn.microsoft.com/en-us/dotnet/csharp/programming-guide/generics/


我很高兴它能帮到你 @zahrakhani - Aravin

5

性能已经在几个答案中被提到作为一个区分因素,但是要回答“ArrayList有多慢?”和“为什么它总体上更慢?”,请看下面。

每当值类型用作元素时,ArrayList的性能会急剧下降。考虑简单添加元素的情况。由于装箱正在进行-因为ArrayList的Add只采用参数-垃圾收集器被触发执行比List<T>更多的工作。

时间差异有多少?至少比List<T>慢几倍。只需看一下将10百万个int值添加到ArrayListList<T>的代码会发生什么:

enter image description here

在“Mean”列中的运行时间差异为5x,突出显示为黄色。还要注意每个突出显示的差异(1000次运行中GC的数量),突出显示为红色。

使用分析器快速查看正在发生什么显示,大多数时间都花在进行GC而不是实际添加元素上。下面的棕色条代表阻塞垃圾收集器活动:

enter image description here

我已经编写了一份关于上述ArrayList场景中发生的事情的详细分析。https://mihai-albert.com/2019/12/15/boxing-performance-in-c-analysis-and-benchmark/

类似的发现在Jeffrey Richter的“CLR通过C#”一书中。从第12章(泛型)开始:

[...] 当我在我的电脑上编译并运行此程序的发布版本(启用优化)时,我得到了下面的输出。 00:00:01.6246959 (GCs= 6) List<Int32>
00:00:10.8555008 (GCs=390) ArrayList of Int32
00:00:02.5427847 (GCs= 4) List<String>
00:00:02.7944831 (GCs= 7) ArrayList of String
这里的输出显示使用泛型List算法和Int32类型要比使用非泛型ArrayList算法和Int32类型快得多。事实上,差距是惊人的:1.6秒对近11秒。这是大约7倍的速度差异!此外,使用值类型(Int32)与ArrayList会导致发生大量装箱操作,从而导致了390次垃圾回收。与此同时,List算法仅需要进行6次垃圾回收。


4

ArrayList是不同类型数据的集合,而List<>是相同类型依赖关系的集合。


3

.NET Framework文档 所述

我们不建议您在新开发中使用 ArrayList 类。相反,我们建议您使用通用的 List<T> 类。 ArrayList 类旨在保存异构对象集合。但是,它并不总是提供最佳性能。 我们建议采用以下方法:

  • 对于异构对象集合,请使用 List<Object>(在C#中)或 List(Of Object)(在Visual Basic中)类型。
  • 对于同构对象集合,请使用 List<T> 类。

另请参见 不应使用非泛型集合

table shows how the non-generic collection types can be replaced by their generic counterparts


3

ArrayList 不是类型安全的,而 List<T> 是类型安全的。简单明了 :)


1
我认为,ArrayListList<T>之间的区别在于:
  1. List<T>, where T is value-type is faster than ArrayList. This is because List<T> avoids boxing/unboxing (where T is value-type).
  2. Many sources say - usually ArrayList used just for backward compatibility. (is not a real difference, but i think it is important note).
  3. Reflection is easier with nongeneric ArrayList then List<T>
  4. ArrayList has IsSynchronized property. So, It is easy to create and use syncronised ArrayList. I didin't found IsSynchronized property for List<T>. Also Keep in mind this type of synchronization is relatively inefficient, msdn):

    var arraylist = new ArrayList();
    var arrayListSyncronized = ArrayList.Synchronized(arraylist
    Console.WriteLine($"syncronized {arraylist.IsSynchronized}");
    Console.WriteLine($"syncronized {arrayListSyncronized.IsSynchronized}");
    
    var list = new List<object>();
    var listSyncronized = ArrayList.Synchronized(list);
    Console.WriteLine($"syncronized {list.IsSynchronized}");//error, no such prop
    Console.WriteLine($"syncronized {list.IsSynchronized}");//error, no such prop
    
  5. ArrayList has ArrayList.SyncRoot property which can be used for syncronisation (msdn). List<T> hasn't SyncRoot property, so in the following construction you need to use some object if you use List<T>:

    ArrayList myCollection = new ArrayList();
    lock(myCollection.SyncRoot) //  ofcourse you can use another object for this goal
    {
        foreach (object item in myCollection)
        {
            // ...
        }
    }
    

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接