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

时间:2009-01-12 08:08:33

标签: .net arrays list

MyClass[] array;
List<MyClass> list;

当一个人优于另一个人时,有哪些情景?为什么?

17 个答案:

答案 0 :(得分:525)

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

List<T>提供了比数组更多的 lot 功能(虽然LINQ使它平均化了一点),并且几乎总是正确的选择。当然,除params个参数外。 ;-p

作为反击 - List<T>是一维的;在哪里 - 因为你有像int[,]string[,,]这样的矩形(等)数组 - 但是在对象模型中还有其他方法可以建模这些数据(如果需要)。

另见:

也就是说,我在protobuf-net项目中使用了很多的数组;完全是为了表现:

  • 它会进行大量的位移,因此byte[]对编码非常重要;
  • 我使用了一个本地滚动byte[]缓冲区,我在填充之前将其发送到底层流(和v.v.);比BufferedStream等更快;
  • 它在内部使用基于数组的对象模型(Foo[]而不是List<Foo>),因为一旦构建就会修复大小,并且需要非常快。

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

答案 1 :(得分:114)

真的只是回答添加我感到惊讶的链接尚未被提及:Eric的Lippert在"Arrays considered somewhat harmful."上的博客文章

你可以从标题中判断它建议在任何实际情况下使用集合 - 但正如Marc正确指出的那样,有很多地方阵列确实是唯一可行的解​​决方案。

答案 2 :(得分:45)

在处理以下数据时使用数组:

  • 固定大小,或不太可能增长
  • 适当大(超过10,50,100个元素,取决于算法)
  • 你会对它进行大量索引,即你知道你会经常想要第三个元素,或者第五个元素,或者其他什么。

使用以下列表:

  • 可变长度数据列表
  • 主要用作堆栈或队列,或者需要完整地迭代
  • 当你不想编写一个表达式来获得声明的最终数组大小而你不想浪费地选择大量数据时

使用hashmap:

  • 可变长度数据列表
  • 需要像数组一样索引

实际上,几乎所有时间都需要列表或散列图。下次选择数据结构时,请考虑它必须为您做好的事情(或者您的代码,无论如何)。然后根据它选择一些东西。如果有疑问,选择尽可能通用的东西,即一个接口,你可以很容易地取代实现。其他答案也有一些很好的联系。

答案 3 :(得分:18)

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

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

答案 4 :(得分:11)

除非你真的关心性能,我的意思是“你为什么使用.Net而不是C ++?”你应该坚持使用List&lt;&gt ;.它更容易维护,并为您在幕后调整阵列大小的所有脏工作。 (如果有必要,List&lt;&gt;非常聪明地选择数组大小,因此通常不需要。)

答案 5 :(得分:6)

当集合本身的不变性是客户与客户之间合同的一部分时, 应优先使用List。提供者代码(不一定是集合中项目的不变性)和当IEnumerable不合适时。

例如,

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的情况下,它并不是什么大问题,但在几乎所有其他情况下它都是一个大问题。将List设置为只读确实有帮助,但会导致运行时错误,而不是编译时。

答案 6 :(得分:3)

如果我确切地知道我需要多少元素,比如说我需要5个元素而且永远 5元素然后我使用数组。否则,我只使用List&lt; T&gt;。

答案 7 :(得分:3)

大多数情况下,使用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代替。

与往常一样,如果内存或性能存在问题,请进行测量!否则你可能会对代码做出错误的假设。

答案 8 :(得分:1)

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

尽管数组不可调整大小,但是使用代码存储数组引用以及正在使用的元素数量并不困难,并根据需要将数组替换为更大的数组。或者,可以轻松地为类似于List<T>但是暴露其后备存储的类型编写代码,从而允许人们说出MyPoints.Add(nextPoint);MyPoints.Items[23].X += 5;。请注意,如果代码尝试访问超出列表末尾的访问权限,则后者不一定会抛出异常,但在概念上使用方式与List<T>非常相似。

答案 9 :(得分:1)

由于没人提及:在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>声明为呼叫者&#39;便利性。

详细说明:

答案 10 :(得分:1)

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

答案 11 :(得分:0)

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

在托管代码中,大多数情况下,您都希望尽可能轻松地使用集合,而不是担心微观优化。

答案 12 :(得分:0)

它完全取决于需要数据结构的上下文。例如,如果要创建要由其他功能或服务使用的项目,则使用List是实现它的完美方式。

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

答案 13 :(得分:0)

他们可能不受欢迎,但我是游戏项目中Arrays的粉丝。 - 在某些情况下,迭代速度可能很重要,如果你没有为每个元素做很多事情,那么对数组的foreach开销要小得多 - 使用辅助函数添加和删除并不是那么难   - 它的速度较慢,但​​是只有在你可能无关紧要的情况下才建造它 - 在大多数情况下,浪费更少的额外内存(仅对结构数组有显着意义) - 稍微减少垃圾,指针和指针追逐

话虽这么说,我在实践中使用List的次数远远超过Arrays,但它们各有其位置。

如果列出了内置类型,那么它们可以优化包装器和枚举开销。

答案 14 :(得分:0)

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

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();

答案 15 :(得分:0)

阵列与列表是经典的可维护性与性能问题。几乎所有开发人员都遵循的经验法则是,您应该同时为两者射击,但是当他们发生冲突时,请选择可维护性而不是性能。该规则的例外是当性能已被证明是一个问题时。如果您将此原则贯彻到Arrays Vs中。列表,那么你得到的是这个:

使用强类型列表,直到遇到性能问题。如果遇到性能问题,请决定是否退出阵列是否会对解决方案的性能带来更大的好处,而不是对维护方面的解决方案造成不利影响。

答案 16 :(得分:-1)

值得一提的是当场铸造的能力。

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[]