首页 技术 正文
技术 2022年11月17日
0 收藏 876 点赞 4,685 浏览 2378 个字

距离上一篇博文,差不多两年了。终于憋出来了一篇。[手动滑稽]

List<>是c#中很常见的一种集合形式,近期在阅读c#源码时,发现了一个很有意思的定义:

    [DebuggerTypeProxy(typeof(Mscorlib_CollectionDebugView<>))]
[DebuggerDisplay("Count = {Count}")]
[Serializable]
public class List<T> : IList<T>, System.Collections.IList, IReadOnlyList<T>
{
private const int _defaultCapacity = 4; private T[] _items;
[ContractPublicPropertyName("Count")]
private int _size;
private int _version;
[NonSerialized]
private Object _syncRoot; static readonly T[] _emptyArray = new T[0]; // Constructs a List. The list is initially empty and has a capacity
// of zero. Upon adding the first element to the list the capacity is
// increased to 16, and then increased in multiples of two as required.
public List() {
_items = _emptyArray;
}
}
...
...
...
private void EnsureCapacity(int min) {
if (_items.Length < min) {
int newCapacity = _items.Length == 0? _defaultCapacity : _items.Length * 2;
if ((uint)newCapacity > Array.MaxArrayLength) newCapacity = Array.MaxArrayLength;
if (newCapacity < min) newCapacity = min;
Capacity = newCapacity;
}
}

咦,_defaultCapacity = 4, _items.Length * 2。抱着怀疑的态度,有了以下这一篇文章。

defaultCapacity=4?

带着怀疑的态度,我们新建一个Console程序,Debug一下。

var list = new List<int>();
Console.WriteLine(list.Capacity);

运行结果:

图片:

…怎么是0呢?一定是我打开的姿势不对,再看一下源码。发现:

static readonly T[]  _emptyArray = new T[0];
...
...
public List() {
_items = _emptyArray;
}

哦,这就对了,初始化时候当然是0。那这个_defaultCapacity有何用?继续看源码。

 if (_items.Length < min) {
int newCapacity = _items.Length == 0? _defaultCapacity : _items.Length * 2;

发现这个三元表达式,为什么要这样做呢?翻了一下google,发现了这样一段文字:

List实例化一个List对象时,Framework只是在内存中申请了一块内存存放List对象本身,系统此时并不知道List会有多少个item元素及元素本身大小。当List添加了第一个item时,List会申请能存储4个item元素的存储空间,此时Capacity是4,当我们添加第五个item时,此时的Capacity就会变成8。也就是当List发现元素的总数大于Capacity数量时,会主动申请且重新分配内存,每次申请的内存数量是之前item数量的两倍。然后将之前所有的item元素复制到新内存。

上面的测试,Capacity=0已经证明了上述这段话的

List实例化一个List对象时,Framework只是在内存中申请了一块内存存放List对象本身,系统此时并不知道List会有多少个item元素及元素本身大小。

接下来我们证明

当List添加了第一个item时,List会申请能存储4个item元素的存储空间,此时Capacity是4

图片:

RT,接下来,我们证明

我们添加第五个item时,此时的Capacity就会变成8。

图片:

RT,的确是这样。

那是否我们得出一个结论,因为不定长的List在Add的时候,频繁的重新申请、分配内存、复制到新内存,效率是否还可以再提升一下呢?

我们先试一下

for (int i = 0; i < count; i++)
{
var listA = new List<int>(10);
listA.Add(i);
}
循环次数 定长长度 运行时间
100 0 144
100 5 23
100 6 49
100 7 45
100 8 73
100 9 21
100 10 22

运行结果:注定长为0表示未设置List长度

循环次数 定长长度 运行时间
10000 0 3741
10000 5 3934
10000 6 4258
10000 7 4013
10000 8 4830
10000 9 4159
10000 10 2370

好吃鲸…为啥9和10差距这么多。。。

我们加大循环次数。结果:

循环次数 定长长度 运行时间
1000000 0 317590
1000000 5 263378
1000000 6 150444
1000000 7 157317
1000000 8 139041
1000000 9 124714
1000000 10 120547

随着循环次数、定长的增加,可以看出,频繁的重新申请、分配内存、复制到新内存,是很耗费时间和性能的。

在以后的工作中,如果有频繁的List.Add,特别是循环Add,不妨考虑一下给List设置一个定长。

相关推荐
python开发_常用的python模块及安装方法
adodb:我们领导推荐的数据库连接组件bsddb3:BerkeleyDB的连接组件Cheetah-1.0:我比较喜欢这个版本的cheeta…
日期:2022-11-24 点赞:878 阅读:9,000
Educational Codeforces Round 11 C. Hard Process 二分
C. Hard Process题目连接:http://www.codeforces.com/contest/660/problem/CDes…
日期:2022-11-24 点赞:807 阅读:5,512
下载Ubuntn 17.04 内核源代码
zengkefu@server1:/usr/src$ uname -aLinux server1 4.10.0-19-generic #21…
日期:2022-11-24 点赞:569 阅读:6,358
可用Active Desktop Calendar V7.86 注册码序列号
可用Active Desktop Calendar V7.86 注册码序列号Name: www.greendown.cn Code: &nb…
日期:2022-11-24 点赞:733 阅读:6,141
Android调用系统相机、自定义相机、处理大图片
Android调用系统相机和自定义相机实例本博文主要是介绍了android上使用相机进行拍照并显示的两种方式,并且由于涉及到要把拍到的照片显…
日期:2022-11-24 点赞:512 阅读:7,771
Struts的使用
一、Struts2的获取  Struts的官方网站为:http://struts.apache.org/  下载完Struts2的jar包,…
日期:2022-11-24 点赞:671 阅读:4,849