MyClass[] array;
List<MyClass> list;
一个比另一个更可取的情况是什么?又为什么呢
MyClass[] array;
List<MyClass> list;
一个比另一个更可取的情况是什么?又为什么呢
Answers:
实际上,很少要使用数组。绝对List<T>
要在任何时候添加/删除数据,因为调整数组的大小很昂贵。如果您知道数据是固定长度的,并且出于某些非常特定的原因(基准化之后)想要进行微优化,则数组可能会很有用。
List<T>
提供一个很多比数组更多的功能(尽管LINQ找齐它一点),而且几乎总是正确的选择。params
当然,除了争论。;-p
作为计数器- List<T>
是一维的;where-as具有矩形int[,]
或类似的数组,例如string[,,]
- 或-,但是还有其他方法可以在对象模型中对此类数据进行建模(如果需要)。
也可以看看:
也就是说,我赚了很多在protobuf-net项目中使用了数组;完全出于性能考虑:
byte[]
对于编码非常重要;byte[]
缓冲区,在向下发送到基础流(和vv)之前先填充该缓冲区;比BufferedStream
等更快Foo[]
而不是List<Foo>
),因为大小一旦建立便是固定的,并且需要非常快。但这绝对是一个例外。对于一般的业务流程处理,List<T>
每次都会取胜。
真正回答只是添加一个令我惊讶的链接还没有被提及:Eric的Lippert的博客条目 “数组被认为有些有害”。
您可以从标题中判断出它建议在可行的地方使用集合-但正如Marc正确指出的那样,在很多地方,数组实际上是唯一可行的解决方案。
尽管有其他建议List<T>
,但在处理以下内容时仍要使用数组:
List<T>
here而不是字节数组呢?
数组应该当集合本身的不可变性是客户端与提供者代码之间的合同的一部分(不一定是集合中项目的不可变性)并且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');
在这种情况下,仅凭该代码片段还不清楚insert方法是否会改变原始的“ str”对象。它需要String的实现级别的知识才能进行确定,这破坏了“按合同设计”方法。对于String而言,这没什么大不了的,但是在几乎所有其他情况下,这可能都是大问题。将列表设置为只读确实有帮助,但会导致运行时错误,而不是编译时错误。
To
创建一个无法修改原始实例的对象,strChars as char[]
而如果该方法有效,则表明您现在可以修改原始对象。
str
内部使用一个数组并ToCharArray
返回对该数组的引用,则str
即使大小保持固定,客户端也可以通过更改该数组的元素来进行更改。但是,您写道“很明显,对“ strChars”的修改不会使原始的“ str”对象发生变异”。我在这里想念什么?从我看到的情况来看,无论哪种情况,客户端都可以访问内部表示,并且无论类型如何,这都将允许某种形式的突变。
在大多数情况下,使用a List
就足够了。A List
使用内部数组来处理其数据,并在List
向其添加比当前容量更多的元素时自动调整数组的大小,这使其比需要事先了解容量的数组更易于使用。
有关C#中的列表或反编译的更多信息,请参见http://msdn.microsoft.com/zh-cn/library/ms379570(v=vs.80).aspx#datastructures20_1_topic5System.Collections.Generic.List<T>
。
如果您需要多维数据(例如,使用矩阵或在图形编程中使用),则可能需要使用 array
改用。
与往常一样,如果内存或性能成为问题,请对其进行衡量!否则,您可能会对代码做出错误的假设。
尚未提及的另一种情况是,当一个项目具有大量项目时,每个项目都由固定在一起的一束相关但独立的变量组成(例如,一个点的坐标或3d三角形的顶点)。暴露场结构的数组将允许其元素“就地”有效地修改-这是任何其他集合类型都不可能实现的。由于结构的数组将其元素连续保存在RAM中,因此对数组元素的顺序访问会非常快。在代码需要多次顺序通过数组的情况下,结构数组的性能可能比数组或类对象引用的其他集合的性能高2:1;进一步,
尽管数组不可调整大小,但是让代码存储数组引用以及正在使用的元素数量并不困难,并根据需要用更大的数组替换数组。另外,一个人可以很容易地为某种类型的代码编写代码,该代码的行为很像a,List<T>
但是暴露了其后备存储,因此可以说出MyPoints.Add(nextPoint);
or 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;
将3添加到列表的第一个元素的X属性。和list
是List<Point>
和Point
是具有X和Y属性的类
他们可能不受欢迎,但我是游戏项目中Array的粉丝。-迭代速度在某些情况下可能很重要,如果您不对每个元素进行太多操作,则数组上的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();
由于没有人提及:在C#中,数组是一个列表。MyClass[]
并且List<MyClass>
都实现了IList<MyClass>
。(例如,void Foo(IList<int> foo)
可以称为Foo(new[] { 1, 2, 3 })
或Foo(new List<int> { 1, 2, 3 })
)
因此,如果编写的方法接受a List<MyClass>
作为参数,但仅使用功能的子集,则可能需要声明为IList<MyClass>
以方便调用者。
细节:
List
,它仅实现IList
接口。
值得一提的是当场铸造的能力。
interface IWork { }
class Foo : IWork { }
void Test( )
{
List<Foo> bb = new List<Foo>( );
// Error: CS0029 Cannot implicitly convert type 'System.Collections.Generic.List<Foo>' to 'System.Collections.Generic.List<IWork>'
List<IWork> cc = bb;
Foo[] bbb = new Foo[4];
// Fine
IWork[] ccc = bbb;
}
因此,在函数的返回类型或参数中使用Array时,它提供了更多的灵活性。
IWork[] GetAllWorks( )
{
List<Foo> fooWorks = new List<Foo>( );
return fooWorks.ToArray( ); // Fine
}
void ExecuteWorks( IWork[] works ) { } // Also accept Foo[]