MyClass[] array;
List<MyClass> list;
一种比另一种更可取的情况是什么?为什么?
实际上,您很少会想要使用数组。在您想要添加/删除数据的任何时候都绝对使用 List<T>
,因为调整数组大小的成本很高。如果您知道数据是固定长度的,并且您想出于某些非常具体的原因(在基准测试之后)进行微优化,那么数组可能会很有用。
List<T>
提供了比数组更多很多的功能(尽管 LINQ 稍微平衡了一点),而且几乎总是正确的选择。当然,除了 params
参数。 ;-p
作为计数器 - List<T>
是一维的; where-因为你有像 int[,]
或 string[,,]
这样的矩形(等)数组 - 但是还有其他方法可以在对象模型中对此类数据进行建模(如果需要)。
也可以看看:
如何/何时放弃在 c#.net 中使用数组?
数组,有什么意义?
也就是说,我在我的 protobuf-net 项目中大量使用了数组;完全出于性能:
它做了很多位移,因此 byte[] 对于编码非常重要;
我使用本地滚动字节 [] 缓冲区,在发送到底层流(和 vv)之前填充该缓冲区;比 BufferedStream 等更快;
它在内部使用基于数组的对象模型(Foo[] 而不是 List
但这绝对是个例外;对于一般业务线处理,List<T>
每次都会获胜。
真的只是回答添加一个我很惊讶的链接还没有被提及:Eric's Lippert's blog entry on "Arrays considered somewhat harmful."
您可以从标题中判断它建议在任何可行的地方使用集合——但正如 Marc 正确指出的那样,在很多地方数组确实是唯一实用的解决方案。
尽管其他答案推荐 List<T>
,但您在处理时需要使用数组:
图像位图数据
其他低级数据结构(即网络协议)
List<T>
而不是字节数组?
除非你真的关心性能,我的意思是,“你为什么使用 .Net 而不是 C++?”你应该坚持使用 List<>。它更易于维护,并为您在幕后完成调整数组大小的所有繁琐工作。 (如有必要, List<> 在选择数组大小方面非常聪明,因此通常不需要。)
当集合本身的不变性是客户端和提供者代码之间合同的一部分(不一定是集合中项目的不变性)并且 IEnumerable 不适合时,应优先使用数组而不是 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 的情况下,这没什么大不了的,但在几乎所有其他情况下它都可能是个大问题。将列表设置为只读确实有帮助,但会导致运行时错误,而不是编译时错误。
To
开头的方法将创建一个无法修改原始实例的对象,而不是 strChars as char[]
,如果有效则表明您现在可以修改原始实例目的。
str
在内部使用数组并且 ToCharArray
返回对该数组的引用,则客户端可以通过更改该数组的元素来改变 str
,即使大小保持固定。然而你写了'很明显,修改“strChars”不会改变原始的“str”对象'。我在这里想念什么?从我所见,在任何一种情况下,客户端都可以访问内部表示,并且无论类型如何,这都会允许某种类型的突变。
如果我确切地知道我需要多少个元素,比如说我需要 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
。
与往常一样,如果内存或性能是一个问题,请测量它!否则,您可能会对代码做出错误的假设。
数组与。列表是一个经典的可维护性与性能问题。几乎所有开发人员都遵循的经验法则是,您应该同时兼顾两者,但是当它们发生冲突时,请选择可维护性而不是性能。该规则的例外情况是性能已经被证明是一个问题。如果你把这个原则带到 Arrays Vs.列表,那么你得到的是这样的:
使用强类型列表,直到遇到性能问题。如果您遇到性能问题,请决定退出阵列是否会使您的解决方案在性能方面受益,而不是在维护方面对您的解决方案不利。
另一种尚未提及的情况是当一个人有大量项目时,每个项目都由一组固定的相关但独立的变量粘在一起(例如,点的坐标,或 3d 三角形的顶点)。一组暴露字段结构将允许其元素被有效地“就地”修改——这是任何其他集合类型都不可能实现的。因为结构数组在 RAM 中连续保存其元素,所以对数组元素的顺序访问可以非常快。在代码需要对数组进行多次顺序传递的情况下,结构数组的性能可能比数组或其他类对象引用集合的性能高出 2:1;此外,就地更新元素的能力可能使结构数组的性能优于任何其他类型的结构集合。
尽管数组不可调整大小,但不难让代码存储数组引用以及正在使用的元素数量,并根据需要用更大的数组替换数组。或者,可以轻松地为一种行为类似于 List<T>
但暴露其后备存储的类型编写代码,从而允许使用 MyPoints.Add(nextPoint);
或 MyPoints.Items[23].X += 5;
。请注意,如果代码尝试访问列表末尾之外的内容,后者不一定会引发异常,但在概念上的用法与 List<T>
非常相似。
Point[] arr;
,代码可以说,例如arr[3].x+=q;
。使用例如 List<Point> list
,有必要改为使用 Point temp=list[3]; temp.x+=q; list[3]=temp;
。如果 List<T>
有方法 Update<TP>(int index, ActionByRefRef<T,TP> proc, ref TP params)
会很有帮助。并且编译器可以将 list[3].x+=q;
变成 {list.Update(3, (ref int value, ref int param)=>value+=param, ref q);
但不存在这样的功能。
list[0].X += 3;
将向列表第一个元素的 X 属性添加 3。 list
是 List<Point>
,Point
是具有 X 和 Y 属性的类
与其比较每种数据类型的特性,我认为最实用的答案是“差异对于您需要完成的工作可能并不那么重要,特别是因为它们都实现了 IEnumerable
,所以请遵循流行的约定并使用 List
直到您有理由不这样做,此时您可能有理由在 List
上使用数组。”
大多数情况下,在托管代码中,您希望集合尽可能易于使用,而不是担心微优化。
.NET 中的列表是数组的包装器,并在内部使用数组。列表操作的时间复杂度与数组相同,但是所有添加的功能/列表的易用性(例如自动调整大小和列表类附带的方法)都会产生更多开销。几乎,我建议在所有情况下都使用列表,除非有令人信服的理由不这样做,例如,如果您需要编写极其优化的代码,或者正在使用其他围绕数组构建的代码。
由于没有人提到:在 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>
。
细节:
为什么数组实现IList?
C# 中的数组如何部分实现 IList
List
,它只实现 IList
接口。
它们可能不受欢迎,但我是游戏项目中数组的粉丝。 - 在某些情况下,迭代速度可能很重要,如果您对每个元素做的不多,则数组上的 foreach 开销显着减少 - 使用辅助函数添加和删除并不难 - 它较慢,但在您只构建一次的情况下可能没关系 - 在大多数情况下,浪费的额外内存更少(仅对结构数组非常重要) - 垃圾和指针以及指针追逐稍微减少
话虽如此,在实践中,我使用 List 的次数远远多于 Arrays,但它们各有各的位置。
如果 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();
请记住,使用 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
new List<T>(int)
构造函数的误解。
它完全取决于需要数据结构的上下文。例如,如果您正在创建项目以供其他功能或服务使用,那么使用 List 是完成它的完美方式。
现在,如果您有一个项目列表并且只想显示它们,例如在网页数组上是您需要使用的容器。
IEnumerable<T>
的迭代器块 - 然后我可以流式传输对象而不是缓冲它们。