【问题标题】:What is the difference between List (of T) and Collection(of T)?List (of T) 和 Collection(of T) 有什么区别?
【发布时间】:2010-09-28 18:33:33
【问题描述】:

我已经看到它们以许多相同的方式使用,我担心如果我不更好地理解这一点,我将走上一条不可逆转的设计道路。另外,我正在使用 .NET。

【问题讨论】:

    标签: .net list collections


    【解决方案1】:

    在 C# 中,存在三个表示对象包的概念。按功能递增的顺序,它们是:

    • 可枚举 - 无序、不可修改
    • 收藏 - 可以添加/删除项目
    • 列表 - 允许项目有顺序(按索引访问和删除)

    可枚举没有顺序。您不能在集合中添加或删除项目。您甚至无法计算集合中的项目数。它严格地允许您一个接一个地访问集合中的每个项目。

    Collection 是一个可修改的集合。您可以从集合中添加和删除对象,还可以获取集合中项目的计数。但是仍然没有顺序,因为没有顺序:无法通过索引访问项目,也无法排序。

    List 是一组有序的对象。您可以对列表进行排序、按索引访问项目、按索引删除项目。

    事实上,在查看这些接口时,它们是相互构建的:

    • interface IEnumerable<T>

      • GetEnumeration<T>
    • interface ICollection<T> : IEnumerable<T>

      • Add
      • Remove
      • Clear
      • Count
    • interface IList<T> : ICollection<T>

      • Insert
      • IndexOf
      • RemoveAt

    在声明变量,或者方法参数的时候,应该选择使用

    • IEnumerable
    • ICollection
    • 列表

    基于您在概念上需要对一组对象执行的操作。

    如果你只需要能够对列表中的每个对象做某事,那么你只需要IEnumerable

    void SaveEveryUser(IEnumerable<User> users)
    {
        for User u in users
          ...
    }
    

    您不关心用户是否保存在List&lt;T&gt;Collection&lt;T&gt;Array&lt;T&gt; 或其他任何地方。您只需要IEnumerable&lt;T&gt; 接口。

    如果您需要能够添加、删除或计算集合中的项目,请使用集合

    ICollection<User> users = new Collection<User>();
    users.Add(new User());
    

    如果您关心排序顺序,并且需要正确的顺序,请使用 List

    IList<User> users = FetchUsers(db);
    

    图表形式:

    | Feature                | IEnumerable<T> | ICollection<T> | IList<T> |
    |------------------------|----------------|----------------|----------|
    | Enumerating items      | X              | X              | X        |
    |                        |                |                |          |
    | Adding items           |                | X              | X        |
    | Removing items         |                | X              | X        |
    | Count of items         |                | X              | X        |
    |                        |                |                |          |
    | Accessing by index     |                |                | X        |
    | Removing by index      |                |                | X        |
    | Getting index of item  |                |                | X        |
    

    System.Collections.Generic 中的List&lt;T&gt;Collection&lt;T&gt; 是实现这些接口的两个类;但它们不是唯一的类:

    因此您可以轻松更改:

    IEnumerable<User> users = new SynchronizedCollection<User>();
    
    SaveEveryUser(users);
    

    tl;博士

    • 可枚举 - 访问项目,无序,不可修改
    • 集合 - 可以修改(添加、删除、计数)
    • 列表 - 可以按索引访问

    选择你需要的概念,然后使用匹配的类。

    【讨论】:

    • OP 询问了具体类型,您已经比较了接口。具体类型 Collection 实现 IList 并具有按索引访问的能力。
    • 答案很好,但偏离了问题。无法满足您对 Collection 和 List 类的回答,我的意思是,如果我试图验证您的观点,他们只是不要辩解。对于一般目的的答案,您可能是正确的,集合没有排序,因此没有索引,但列表是有序的,因此可以在某个索引处插入。
    • 如果列表是有序的,而集合是无序的,那将是一个巨大的功能差异,可以轻松引导新学习者(如我)进行选择。但是等等,你为什么说一个集合没有顺序?它提供了IndexOf()RemoveAt() 方法,所以它是有序的,不是吗?我错过了什么吗?
    • @RayLuo 我特指ICollection&lt;T&gt;IList&lt;T&gt;。不同的具体实现可能表现不同。例如,如果您通过其IEnumerable&lt;T&gt; 接口访问List&lt;T&gt;,那么您将无法添加、删除、排序或计算列表中的项目。
    • 这个答案很有帮助!有点偏离问题,但这是我试图搜索的内容。
    【解决方案2】:

    除了其他答案之外,我还对通用列表和集合功能进行了快速概述。集合是列表的有限子集:

    * = 存在 o = 部分存在 属性/方法集合 列表 ---------------------------------------------- Add() * * AddRange() * AsReadOnly() * BinarySearch() * Capacity * Clear() * * Contains() * * ConvertAll() * CopyTo() o * Count * * Equals() * * Exists() * Find() * FindAll() * FindIndex() * FindLast() * FindLastIndex() * ForEach() * GetEnumerator() * * GetHashCode() * * GetRange() * GetType() * * IndexOf() o * Insert() * * InsertRange() * Item() * * LastIndexOf() * New() o * ReferenceEquals() * * Remove() * * RemoveAll() * RemoveAt() * * RemoveRange() * Reverse() * Sort() * ToArray() * ToString() * * TrimExcess() * TrueForAll() *

    【讨论】:

      【解决方案3】:

      根据 MSDN,List(Of T).Add 是“一个 O(n) 操作”(当超出“容量”时),而 Collection(Of T).Add 是 always“一个O(1) 操作”。如果 List 是使用 Array 实现的,而 Collection 是 Linked List,那将是可以理解的。但是,如果是这种情况,人们会期望 Collection(Of T).Item 是“一个 O(n) 操作”。但是 - 它是 - 不是!?! Collection(Of T).Item 是“O(1) 操作”,就像 List(Of T).Item。

      最重要的是,“tuinstoel”的“2008 年 12 月 29 日 22:31”帖子声称速度测试显示 List(Of T).Add 比 Collection(Of T).Add 快已经用 Long 和 String 复制了。虽然我只比他声称的 80% 快了 33%,但根据 MSDN,它应该是相反的“n”倍!?!

      【讨论】:

        【解决方案4】:

        所有这些接口都继承自IEnumerable,您应该确保自己理解。该接口基本上允许您在 foreach 语句中使用该类(在 C# 中)。

        • ICollection 是您列出的最基本的接口。这是一个支持Count 的可枚举接口,仅此而已。
        • IListICollection 的一切,但它也支持添加和删除项目,按索引检索项目等。它是“对象列表”最常用的接口,我知道这是模糊的。
        • IQueryable 是一个支持 LINQ 的可枚举接口。您始终可以从 IList 创建 IQueryable 并使用 LINQ to Objects,但您也发现 IQueryable 用于延迟执行 LINQ to SQL 和 LINQ to Entities 中的 SQL 语句。
        • IDictionary 是一种不同的动物,因为它是唯一键到值的映射。它也是可枚举的,因为您可以枚举键/值对,但除此之外,它的用途与您列出的其他用途不同

        【讨论】:

        【解决方案5】:

        列表速度更快。

        举个例子

        private void button1_Click(object sender, EventArgs e)
        {
          Collection<long> c = new Collection<long>();
          Stopwatch s = new Stopwatch();
          s.Start();
          for (long i = 0; i <= 10000000; i++)
          {
            c.Add(i);
          }
          s.Stop();
          MessageBox.Show("collect " + s.ElapsedMilliseconds.ToString());
        
          List<long> l = new List<long>();
          Stopwatch s2 = new Stopwatch();
          s2.Start();
          for (long i = 0; i <= 10000000; i++)
          {
            l.Add(i);
          }
          s2.Stop();
          MessageBox.Show("lis " + s2.ElapsedMilliseconds.ToString());
        
        
        }
        

        在我的机器上List&lt;&gt; 几乎快两倍。

        编辑

        我不明白为什么人们不赞成这一点。在我的工作机器和家用机器上,List 代码都快了 80%。

        【讨论】:

        • 如何更快?抬头?插入?移动?搜索?为什么它更快?
        • List 要输入的字母比 Collection 少:)
        • Collection 的方法较少。因此它更快。 QED。 (开玩笑,我不是拖钓)
        • 在我的机器上试了一下,list 快了大约 20%。有兴趣讨论为什么会这样。也许列表在分配内存方面会更好。
        • List 的方法是不可继承的,所以没有检查它们是否被继承;集合的方法是可继承的。优点是您可以使用 Collection 作为基类来继承和创建自定义 Collection。
        【解决方案6】:

        Collection&lt;T&gt; 是围绕IList&lt;T&gt; 的可定制包装器。虽然IList&lt;T&gt; 没有密封,但它不提供任何自定义点。 Collection&lt;T&gt; 的方法默认委托给标准的IList&lt;T&gt; 方法,但可以很容易地重写以执行您想要的操作。也可以在 Collection&lt;T&gt; 中连接事件,我认为 IList 无法完成。

        简而言之,事后扩展它要容易得多,这可能意味着更少的重构。

        【讨论】:

        • @Marc Gravell, @Adam Lassek:我们不能通过使用 public void new InsertItem(...) 隐藏 InsertItem(...) 方法来对 List 做同样的事情,然后从内部调用 base.InsertItem(...) ?它仍然不会破坏合同。 (列表中的名称是“插入”,但仍然如此)。那么坚持 Collections 有什么大不了的呢?
        • @DeeStackOverflow - 因为任何使用不同 API 的代码都不会使用方法隐藏 - 即任何寻找 IListIList&lt;T&gt;List&lt;T&gt; 等的代码。简而言之,您不知道它是否会被调用。多态性解决了这个问题。
        • @AdamLassek:您可能希望在您的回答中添加关于 ObservableCollection&lt;T&gt; 的示例,其中方法被覆盖以通知更改。
        【解决方案7】:

        List&lt;T&gt; 是一个非常常见的容器,因为它非常通用(有很多方便的方法,如 SortFind 等) - 但如果你想覆盖任何行为(例如,在插入时检查项目)。

        Collection&lt;T&gt; 是任何IList&lt;T&gt; 的包装器(默认为List&lt;T&gt;) - 它具有扩展点(virtual 方法),但没有像Find 这样的支持方法那么多。由于是间接的,它比List&lt;T&gt; 稍慢,但也不慢。

        使用 LINQ,List&lt;T&gt; 中的额外方法变得不那么重要了,因为 LINQ-to-Objects 倾向于提供它们...例如First(pred)OrderBy(...) 等。

        【讨论】:

        • Collection 缺少 foreach 方法,即使在 Linq-to-Objects 中也是如此。
        • @tuinstoel - 但添加起来很简单。
        【解决方案8】:

        Hanselman Speaks: "Collection&lt;T&gt; 看起来像一个列表,它内部甚至还有一个 List&lt;T&gt;。每个方法都委托给内部 List&lt;T&gt;。它包括一个受保护的属性,该属性公开了 List&lt;T&gt;。"

        编辑:Collection&lt;T&gt; 在 System.Generic.Collections .NET 3.5 中不存在。如果您从 .NET 2.0 迁移到 3.5,如果您使用大量 Collection&lt;T&gt; 对象,则需要更改一些代码,除非我遗漏了一些明显的东西......

        编辑 2:Collection&lt;T&gt; 现在位于 .NET 3.5 的 System.Collections.ObjectModel 命名空间中。帮助文件是这样写的:

        “System.Collections.ObjectModel 命名空间包含可用作可重用库的对象模型中的集合的类。当属性或方法返回集合时使用这些类。”

        【讨论】:

          【解决方案9】:

          List&lt;T&gt; 供应用程序代码内部使用。您应该避免编写接受或返回 List&lt;T&gt; 的公共 API(考虑改用超类或集合接口)。

          Collection&lt;T&gt; 为自定义集合提供基类(尽管可以直接使用)。

          考虑在您的代码中使用Collection&lt;T&gt;,除非您需要List&lt;T&gt; 的特定功能。

          以上只是建议。

          [改编自:框架设计指南,第二版]

          【讨论】:

          • 值得注意的是,使用任何类型的可变对象来封装它们自己的状态的类型应该避免返回这种类型的对象,除非对象有问题的有一种方法可以在它们发生突变时通知它们的所有者,或者返回对象的方法的名称清楚地暗示它正在返回一个新实例。请注意,例如一个Dictionary&lt;string, List&lt;string&gt;&gt; 来返回List&lt;string&gt; 就可以了,因为字典的状态只封装了列表的identities,而不是它们的内容。
          【解决方案10】:

          这是研究生院的问题之一。 T 的集合有点抽象;可能有默认实现(我不是 .net/c# 人),但集合将具有基本操作,如添加、删除、迭代等。

          T 的列表暗示了这些操作的一些细节:add 应该花费恒定时间,remove 应该花费与元素数量成比例的时间,getfirst 应该是恒定时间。一般来说,List是一种Collection,但Collection不一定是List。

          【讨论】:

            【解决方案11】:

            List 表示一个集合,其中项目的顺序很重要。它还支持方法 s.a.排序和搜索。集合是一种更通用的数据结构,它对数据的假设更少,也支持更少的操作方法。如果您想公开自定义数据结构,您可能应该扩展集合。如果您需要在不暴露数据结构的情况下操作数据,列表可能是更方便的方式。

            【讨论】:

              【解决方案12】:

              两者都实现相同的接口,因此它们的行为方式相同。也许它们在内部的实现方式不同,但这必须进行测试。

              我看到的唯一真正的区别是命名空间以及Collection&lt;T&gt; 标记为ComVisibleAttribute(false),因此 COM 代码无法使用它。

              【讨论】:

              • 它们实现了不同的接口 - List 实现了 IList,而 Collection 没有。
              • @Bevan 在 c# 中尝试一下,它们都实现了相同的接口集
              • 这是一个有趣的变化@KyloRen - 他们现在都实现了相同的接口集; 08 年的情况并非如此。
              • @Bevan 很有趣。不确定两个不同类的原因是什么,一个只有一些额外的方法。
              猜你喜欢
              • 1970-01-01
              • 1970-01-01
              • 1970-01-01
              • 1970-01-01
              • 1970-01-01
              • 1970-01-01
              • 2021-05-25
              • 2010-10-02
              • 2012-03-10
              相关资源
              最近更新 更多