数组与列表 < T > : 何时使用哪个?

MyClass[] array;
List<MyClass> list;

什么情况下一个比另一个更好? 为什么?

386690 次浏览

实际上,你很少想使用数组。任何时候你想添加/删除数据时,一定要使用List<T>,因为调整数组的大小是昂贵的。如果你知道数据是固定长度的,并且你想出于某些非常具体原因(在基准测试之后)进行微优化,那么数组可能会很有用。

List<T>提供了比数组更多的很多功能(尽管LINQ使其更加均衡),并且几乎总是正确的选择。当然,除了params参数。;-p

作为计数器-List<T>是一维的;其中-因为您有像int[,]string[,,]这样的矩形(等)数组-但是还有其他方法可以在对象模型中对此类数据进行建模(如果需要)。

另见:

也就是说,我在我的原型网项目中使用了很多数组;完全是为了性能:

  • 它做了很多位移动,所以byte[]对于编码来说非常重要;
  • 我使用本地滚动byte[]缓冲区,在发送到底层流(和v. v.)之前填充;比BufferedStream等快;
  • 它在内部使用基于数组的对象模型(Foo[]而不是List<Foo>),因为大小在构建后是固定的,并且需要非常快。

但这绝对是一个例外;对于一般业务线处理,List<T>每次都赢。

除非你真的关心性能,我的意思是,“你为什么使用. Net而不是C++?”你应该坚持使用List<>。它更容易维护,并且在幕后为你完成调整数组大小的所有肮脏工作。(如果有必要,List<>在选择数组大小方面非常聪明,所以通常不需要。)

这完全取决于需要数据结构的上下文。例如,如果您要创建要被其他函数或服务使用的项目,使用List是完成它的完美方法。

现在,如果您有一个项目列表并且您只想显示它们,那么您需要使用的容器就是网页数组。

尽管其他答案推荐List<T>,但您在处理时需要使用数组:

  • 图像位图数据
  • 其他低级数据结构(即网络协议)

真的只是回答添加一个链接,我很惊讶还没有提到:Eric的Lippert关于“数组被认为有些有害。”的博客条目

你可以从标题中判断,它建议在任何可行的地方使用集合——但正如Marc正确指出的那样,在很多地方,数组确实是唯一实用的解决方案。

如果我确切地知道我需要多少元素,假设我需要5个元素并且只有曾经 5个元素,那么我使用数组。否则我只使用List

大多数时候,使用List就足够了。List使用内部数组来处理其数据,并在向List添加比其当前容量更多的元素时自动调整数组的大小,这使得它比数组更容易使用,在数组中你需要事先知道容量。

有关C#中列表的更多信息,请参阅http://msdn.microsoft.com/en-us/library/ms379570(v=vs.80). aspx#datastructures20_1_topic5,或者只是反编译System.Collections.Generic.List<T>

如果你需要多维数据(例如使用矩阵或图形编程),你可能会选择array

与往常一样,如果内存或性能是一个问题,请衡量它!否则您可能会对代码做出错误的假设。

另一种尚未提到的情况是当一个人将有大量的项目,每个项目由一组固定的相关但独立的变量粘在一起(例如点的坐标,或3d三角形的顶点)。暴露字段结构的数组将允许其元素被有效地修改“就地”-这是任何其他集合类型都不可能的。因为一个结构数组将其元素连续保存在RAM中,因此对数组元素的顺序访问可以非常快。在代码需要对数组进行多次顺序传递的情况下,结构数组的性能可能比数组或其他类对象引用集合高出2:1;此外,更新元素的能力可能允许结构数组优于任何其他类型的结构集合。

尽管数组不可调整大小,但让代码存储一个数组引用以及正在使用的元素数量,并根据需要用更大的元素替换数组并不困难。或者,人们可以很容易地为行为很像List<T>但暴露其后备存储的类型编写代码,从而允许人们说MyPoints.Add(nextPoint);MyPoints.Items[23].X += 5;。请注意,如果代码试图访问列表末尾以外的内容,后者不一定会引发异常,但其他用法在概念上与List<T>非常相似。

当集合本身的不变性是客户端和提供者代码之间契约的一部分时(不一定是集合中项目的不变性),以及当不适合使用IENumable时,优先使用数组应该而不是List。

例如,

var str = "This is a string";
var strChars = str.ToCharArray();  // returns array

很明显,“strChars”的修改不会改变原始的“str”对象,无论“str”的底层类型的实现级知识如何。

但假设

var str = "This is a string";
var strChars = str.ToCharList();  // returns List<char>
strChars.Insert(0, 'X');

在这种情况下,仅从该代码片段并不清楚插入方法是否会改变原始的“str”对象。做出这一决定需要String的实现级别知识,这打破了按合同设计的方法。在String的情况下,这不是什么大问题,但几乎在其他任何情况下都可能是一件大事。将List设置为只读确实有帮助,但会导致运行时错误,而不是编译时错误。

与其比较每种数据类型的特性,我认为最实用的答案是“差异可能对你需要完成的事情并不重要,特别是因为它们都实现了IEnumerable,所以遵循流行的惯例并使用List,直到你有理由不这样做,此时你可能会有理由使用数组而不是List

大多数时候,在托管代码中,您会希望集合尽可能易于使用,而不是担心微优化。

. NET中的列表是数组的包装器,并在内部使用数组。对列表进行操作的时间复杂度与数组相同,但是在所有添加的功能/列表的易用性(例如自动调整大小和list类附带的方法)方面会有更多的开销。基本上,我建议在所有情况下都使用列表,除非有令人信服的理由没有这样做,例如如果您需要编写极度优化的代码,或者正在使用围绕数组构建的其他代码。

因为没有人提到:在C#中,数组是一个列表。MyClass[]List<MyClass>都实现了IList<MyClass>。(例如void Foo(IList<int> foo)可以像Foo(new[] { 1, 2, 3 })Foo(new List<int> { 1, 2, 3 })一样调用)

因此,如果您正在编写一个接受List<MyClass>作为参数但仅使用功能子集的方法,您可能希望声明为IList<MyClass>,以方便调用者。

详情:

它们可能不受欢迎,但我是游戏项目中数组的粉丝。 -迭代速度在某些情况下可能很重要,如果您每个元素不做太多工作,则数组上的foreach开销显着减少 -使用辅助函数添加和删除并不难 -速度较慢,但在您只构建一次的情况下可能无关紧要 -在大多数情况下,浪费的额外内存更少(仅对结构数组真正重要) -稍微少一点垃圾和指针和指针追逐

话虽如此,在实践中,我使用List的频率远远超过数组,但它们都有自己的位置。

如果List有一个内置类型,这样他们就可以优化包装器和枚举开销,那就太好了。

填充列表比数组更容易。对于数组,您需要知道数据的确切长度,但对于列表,数据大小可以是任何。并且,您可以将列表转换为数组。

List<URLDTO> urls = new List<URLDTO>();


urls.Add(new URLDTO() {
key = "wiki",
url = "https://...",
});


urls.Add(new URLDTO()
{
key = "url",
url = "http://...",
});


urls.Add(new URLDTO()
{
key = "dir",
url = "https://...",
});


// convert a list into an array: URLDTO[]
return urls.ToArray();

数组vs.列表是一个经典的可运维性与性能问题。几乎所有开发人员都遵循的经验法则是,你应该同时考虑两者,但是当他们发生冲突时,选择可运维性而不是性能。该规则的例外是,当性能已经被证明是一个问题时。如果你将这一原则带到数组vs.列表中,那么你得到的是:

在遇到性能问题之前,请使用强类型列表。如果遇到性能问题,请决定退出数组是否会使您的解决方案在性能上受益,而不是在维护方面损害您的解决方案。

请记住,使用List是不可能做到这一点的:

List<string> arr = new List<string>();


arr.Add("string a");
arr.Add("string b");
arr.Add("string c");
arr.Add("string d");


arr[10] = "new string";

它生成一个异常。

而不是数组:

string[] strArr = new string[20];


strArr[0] = "string a";
strArr[1] = "string b";
strArr[2] = "string c";
strArr[3] = "string d";


strArr[10] = "new string";

但是使用数组没有自动调整数据结构大小。您必须手动或使用Array. Resize方法管理它。

技巧可以是使用空数组初始化List。

List<string> arr = new List<string>(new string[100]);


arr[10] = "new string";

但在这种情况下,如果您使用Add方法放置一个新元素,它将被注入到List的末尾。

List<string> arr = new List<string>(new string[100]);


arr[10] = "new string";


arr.Add("bla bla bla"); // this will be in the end of List