【发布时间】:2018-09-25 18:54:42
【问题描述】:
C# 是否提供了一种直接从数组创建列表而无需逐个复制元素的方法?
我知道在底层,列表只是维护一个数组,因此从一个数组开始并创建一个新列表在内部简单地指向该数组似乎很自然。这样做的好处是在将数组转换为 List 时会大大提高性能。
在 C# 中是否有本地方法可以做到这一点?如果没有,是否有人有扩展方法或库来执行此操作?
Peter 要求量化“性能的巨大提升”。 所以这里是基于我对 List 构造函数如何工作的理解:
List() 包含三个重载。其中一个重载将 IEnumerable 作为输入。这种重载通过声明一个非常小的新列表来工作(除非它在最近的 c# 版本中发生了变化,历史默认大小为 0,然后在首次添加元素时初始化为 4)。
每次将一个元素添加到列表中时,如果新元素超出容量,则通过创建一个全新的数组并将元素一个接一个地复制到新数组,数组大小会增加一倍。这样做的结果是,从 IEnumerable 构建 List 的成本是 O(n) 加上所有发生的数组副本的成本。重点是,不小。
对于 IEnumerable,List() 构造必须以这种方式工作,因为 IEnumerable 的大小未知。如果你从一个数组创建一个 List,你会在创建时知道确切的大小,这意味着成本应该是 O(1)。
【问题讨论】:
-
请量化“性能上的巨大提升”。在大多数情况下,这似乎是过早的优化。
-
接受数组的 List 构造函数将调用 Array.Copy。我怀疑这会 blit 整个数组,而不是一个一个地枚举和复制。
-
如果你愿意做一些性能测试,你可能会发现像
var newList = new List(theArray.Length); newList.AddRange(theArray);这样的东西要快一些(不过,谁知道呢,这可能是System.Array.ToList在幕后所做的 -
我对这个问题有点困惑——
List<T>构造函数是否一个接一个复制项目是一个实现细节。当给定一个数组作为参数时,列表构造函数从旧数组调用CopyTo到新数组,它也应该这样做。CopyTo是如何工作的——无论是一次复制一个项目,还是将它们分批以利用处理器同时复制多个小项目的能力——又是一个实现细节。但该代码应该非常快。 -
我不认为这是一个愚蠢的问题;我确实认为数组几乎从来都不是满足我需求的数据结构,这正是您引用的原因!数组是我不想要的大小固定但内容可变的奇怪组合。十年前我写了一篇关于这个主题的博客文章。 blogs.msdn.microsoft.com/ericlippert/2008/09/22/…