Boa*_*oaz 99 .net c# generics initialization list
.NET提供了一个通用列表容器,其性能几乎相同(请参阅阵列性能与列表问题).但是它们在初始化方面完全不同.
使用默认值很容易初始化数组,根据定义,它们已经具有一定的大小:
string[] Ar = new string[10];
Run Code Online (Sandbox Code Playgroud)
这允许人们安全地分配随机项目,比如说:
Ar[5]="hello";
Run Code Online (Sandbox Code Playgroud)
列表事情更棘手.我可以看到两种方法进行相同的初始化,这两种方式都不是你所谓的优雅:
List<string> L = new List<string>(10);
for (int i=0;i<10;i++) L.Add(null);
Run Code Online (Sandbox Code Playgroud)
要么
string[] Ar = new string[10];
List<string> L = new List<string>(Ar);
Run Code Online (Sandbox Code Playgroud)
什么是更干净的方式?
编辑:到目前为止的答案是指容量,这不是预先填充列表.例如,在刚创建的容量为10的列表中,无法做到L[2]="somevalue"
编辑2:人们想知道为什么我想以这种方式使用列表,因为它不是他们打算使用的方式.我可以看到两个原因:
人们可以非常有说服力地认为列表是"下一代"阵列,增加灵活性几乎不会受到惩罚.因此,默认情况下应该使用它们.我指出它们可能不那么容易初始化.
我目前正在编写的是一个基类,它提供默认功能作为更大框架的一部分.在我提供的默认功能中,List的大小在高级中是已知的,因此我可以使用数组.但是,我想为任何基类提供动态扩展它的机会,因此我选择了一个列表.
小智 115
List<string> L = new List<string> ( new string[10] );
Run Code Online (Sandbox Code Playgroud)
Jon*_*eet 72
我不能说我经常需要这个 - 你能否详细说明你为什么要这样做?我可能会把它作为一个静态方法放在一个帮助器类中:
public static class Lists
{
public static List<T> RepeatedDefault<T>(int count)
{
return Repeated(default(T), count);
}
public static List<T> Repeated<T>(T value, int count)
{
List<T> ret = new List<T>(count);
ret.AddRange(Enumerable.Repeat(value, count));
return ret;
}
}
Run Code Online (Sandbox Code Playgroud)
您可以使用,Enumerable.Repeat(default(T), count).ToList()
但由于缓冲区大小调整,这将是低效的.
编辑:如评论中所述,如果您愿意,可以T
使用循环填充列表.那也会稍快一些.就个人而言,我发现代码使用count
更具描述性,并怀疑在现实世界中性能差异是无关紧要的,但您的里程可能会有所不同.
Ed *_* S. 23
使用以int("capacity")作为参数的构造函数:
List<string> = new List<string>(10);
Run Code Online (Sandbox Code Playgroud)
编辑:我应该补充一点,我同意弗雷德里克.您使用List的方式与首先使用它的整个推理背道而驰.
EDIT2:
编辑2:我目前正在编写的是一个基类,它提供默认功能作为更大框架的一部分.在我提供的默认功能中,List的大小在高级中是已知的,因此我可以使用数组.但是,我想为任何基类提供动态扩展它的机会,因此我选择了一个列表.
为什么有人需要知道具有所有空值的List的大小?如果列表中没有实际值,我希望长度为0.无论如何,这是cludgy的事实表明它违背了类的预期用途.
这是一个老问题,但我有两个解决方案。一是快速而肮脏的反射;另一种是实际回答问题的解决方案(设置大小而不是容量),同时仍然具有高性能,但这里的答案都没有做到这一点。
这既快又脏,而且代码的作用应该非常明显。如果您想加快速度,请缓存 GetField 的结果,或创建一个 DynamicMethod 来执行此操作:
public static void SetSize<T>(this List<T> l, int newSize) =>
l.GetType().GetField("_size", BindingFlags.NonPublic | BindingFlags.Instance).SetValue(l, newSize);
Run Code Online (Sandbox Code Playgroud)
显然很多人会对将这样的代码投入生产犹豫不决。
ICollection<T>
该解决方案基于以下事实:构造函数List(IEnumerable<T> collection)
会优化ICollection<T>
大小并立即将大小调整到正确的量,而无需进行迭代。然后它调用集合CopyTo
来进行复制。
构造函数的代码List<T>
如下:
public List(IEnumerable<T> collection) {
....
ICollection<T> c = collection as ICollection<T>;
if (collection is ICollection<T> c)
{
int count = c.Count;
if (count == 0)
{
_items = s_emptyArray;
}
else {
_items = new T[count];
c.CopyTo(_items, 0);
_size = count;
}
}
Run Code Online (Sandbox Code Playgroud)
因此我们可以完全优化地将 List 预初始化为正确的大小,而无需任何额外的复制。
为何如此?通过创建一个ICollection<T>
除了返回 a 之外不执行任何操作的对象Count
。具体来说,我们不会实现任何CopyTo
调用唯一其他函数的东西。
private struct SizeCollection<T> : ICollection<T>
{
public SizeCollection(int size) =>
Count = size;
public void Add(T i){}
public void Clear(){}
public bool Contains(T i)=>true;
public void CopyTo(T[]a, int i){}
public bool Remove(T i)=>true;
public int Count {get;}
public bool IsReadOnly=>true;
public IEnumerator<T> GetEnumerator()=>null;
IEnumerator IEnumerable.GetEnumerator()=>null;
}
public List<T> InitializedList<T>(int size) =>
new List<T>(new SizeCollection<T>(size));
Run Code Online (Sandbox Code Playgroud)
理论上,我们可以对现有数组做同样的事情AddRange
,InsertRange
这也解释了ICollection<T>
,但是那里的代码为假定的项目创建了一个新数组,然后将它们复制进去。在这种情况下,清空会更快 -环形Add
:
public void SetSize<T>(this List<T> l, int size)
{
if(size < l.Count)
l.RemoveRange(size, l.Count - size);
else
for(size -= l.Count; size > 0; size--)
l.Add(default(T));
}
Run Code Online (Sandbox Code Playgroud)
如果要使用固定值初始化它,为什么使用List?我可以理解 - 为了性能 - 你想给它一个初始容量,但它不是一个列表比常规数组的优点之一,它可以在需要时增长?
当你这样做:
List<int> = new List<int>(100);
Run Code Online (Sandbox Code Playgroud)
您创建一个容量为100整数的列表.这意味着在添加第101个项目之前,您的列表不需要"增长".列表的基础数组将初始化为100.
如果你想用 N 个固定值的元素初始化列表:
public List<T> InitList<T>(int count, T initValue)
{
return Enumerable.Repeat(initValue, count).ToList();
}
Run Code Online (Sandbox Code Playgroud)
首先创建一个数组,其中包含所需的项目数,然后将其转换为列表。
int[] fakeArray = new int[10];
List<int> list = fakeArray.ToList();
Run Code Online (Sandbox Code Playgroud)
初始化列表的内容不是真正的列表.列表旨在容纳对象.如果要将特定数字映射到特定对象,请考虑使用键值对结构,如散列表或字典而不是列表.
您似乎强调需要与数据进行位置关联,那么关联数组不是更合适吗?
Dictionary<int, string> foo = new Dictionary<int, string>();
foo[2] = "string";
Run Code Online (Sandbox Code Playgroud)
归档时间: |
|
查看次数: |
172162 次 |
最近记录: |