Jeffrey Zhao在"你的字典里有多少元素?"一文中,提到了他在面试时问过的一个问题:List<T>是怎么存放元素?不幸的是,自己也回答不出来,只知道怎么用,却不知道为啥这样用,很明显的"知其然而不知其所以然"。于是,扒了一段List<T>的一段源码来一窥究竟。

using System;
using System.Diagnostic;
using System.Collections.ObjectModel;
using System.Security.Permissions; namespace System.Collections.Generic
{
...
[Serializable()]
public class List<t> : IList<t>, System.Collections.IList
{
private const int _defaultCapacity = 4;
private T[] _items; //List<T>内部是依靠数组_items存放数据的
private int _size; //数组的长度
private int _version;
[NoSerialized]
private Object _syncRoot;
static T[] _emptyArray = new T[0]; //无参数构造函数 把_items设置成一个空的数组
public List()
{
_items = _emptyArray;
} //此构造函数 给_items数组一个初始容量
public List(int capacity)
{
...
items = new T[capaicty];
} //此构造函数 把集合类型参数拷贝给_items数组
public List(IEnumerable<t> collection)
{
...
ICollection<t> c = collection as ICollection<t>;
if(c != null)
{
int count = c.Count; //把构造函数集合类型参数的长度赋值给临时变量count
_items = new T[count]; //List<T>内部维护的_items数组的长度和构造函数集合类型参数的长度一致
c.CopyTo(_items, 0); //把构造函数集合的所有元素拷贝到_items数组中去
_size = count; //_items数组的长度就是构造函数集合类型参数的长度
}
else
{
_size = 0;
_items = new T[_defaultCapacity];
...
}
} //通过设置这个属性,改变List<t>内部维护的_items数组的长度
public int Capacity
{
get {return _items.Length; }
set {
if(value != _items.Length){ //如果当前赋值和List<t>维护的内部数组_items长度不一致
if(value < _size){
//TODO: 处理异常
}
if(value > 0){
T[] newItems = new T[value]; //创建一个临时的、新的数组,长度为新的赋值
if(_size > 0){
//把临时的、新的数组拷贝给List<t>内部维护的数组_items,注意,这时_items的长度为新的赋值
Array.Copy(_items, 0, newItems, 0, _size);
}
} else {
_items = _emptyArray;
}
}
}
} public void Add(T item)
{
if(_size == _items.Length) EnsureCapacity(_size + 1);
_items[_size++] = item;
...
} //确保List<t>内部维护的_items数组的长度至少是给定的值
//如果_items数组原先的长度比给定的值小,就让_items数组的长度设置为原先的长度的2倍
privat void EnsureCapacity(int min)
{
if(_items.Length < min){
int newCapacity = _items.Length == 0 ? _defaultCapacity : _items.Legnth * 2;
if(newCapacity < min) newCapacity = min;
Capacity = newCapacity;
}
} }
}

由此可见,向List<T>中存放元素的大致过程是这样的:

→List<T>内部维护着一个数组_items,用来存放T类型的元素。
→当有新的T类型元素存放进来,即调用Add(T item)方法。
→Add(T item)方法内部调用EnsureCapacity(int min)方法确保List<T>的Capaicty属性值至少在原先长度上加1,最多是原先长度的2倍。
→在给Capacity赋值的过程中,对_items的长度进行了扩容。
→扩容后,再把新的T类型元素存放进来。

简单地说:

当有新的元素存放到List<T>中时,List<T>先对其维护的内部数组进行扩容,然后再把新元素放进来。

C#中List<T>是怎么存放元素的的更多相关文章

  1. Leetcode(215)-数组中的第K个最大元素

    在未排序的数组中找到第 k 个最大的元素.请注意,你需要找的是数组排序后的第 k 个最大的元素,而不是第 k 个不同的元素. 示例 1: 输入: [3,2,1,5,6,4] 和 k = 2 输出: 5 ...

  2. PTA 将数组中的数逆序存放

    7-1 将数组中的数逆序存放 (20 分)   本题要求编写程序,将给定的n个整数存入数组中,将数组中的这n个数逆序存放,再按顺序输出数组中的元素. 输入格式: 输入在第一行中给出一个正整数n(1). ...

  3. 在js中怎么样选择互斥的相邻元素

    在使用jquery中,我们通常会选择siblings()去选择相邻元素,使用eq()方法去匹配元素,使用index()获取对应元素的索引值,具体jquery代码如下: <style> *{ ...

  4. 寻找数组中的第K大的元素,多种解法以及分析

    遇到了一个很简单而有意思的问题,可以看出不同的算法策略对这个问题求解的优化过程.问题:寻找数组中的第K大的元素. 最简单的想法是直接进行排序,算法复杂度是O(N*logN).这么做很明显比较低效率,因 ...

  5. IT公司100题-35- 求一个矩阵中最大的二维矩阵(元素和最大)

    问题描述: 求一个矩阵中最大的二维矩阵(元素和最大).如: 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 中最大的是: 4 5 9 10   分析: 2*2子数组的最大和.遍历求和,时 ...

  6. PHP中常用的正则表达式由哪些元素构成?

    在程序开发中,我们常常要用到正则表达式,对于新手来说,很多时候知道正则表达式是怎么回事,但当真正需要使用的时候,却不知该用什么函数,具体的修饰符也比较混乱.下面小编就为大家整理了一些php正则表达式中 ...

  7. HTML 之 Web页面表单form中只有一个input的text元素,按回车默认提交

    WEB开发中,如果页面的 form 中只有一个input元素,在该input元素的输入框中按回车(注:此时并没有写对应的onkeydown等事件处理),则浏览器会默认提交表单,请看如下代码: < ...

  8. C:函数:功能:实现字符数组中所有字母的倒序存放并输出

    前两天小测碰到一道题,建立一个函数,功能:实现字符数组中所有字母的倒序存放并输出,一开始觉得简单跟数字数组差不多,运行一下发现很多格式错误,这些是不必要的错误,现在就来说下,先说一下代码思路:定义一个 ...

  9. CSS中伪类选择器及伪元素

    1.伪类选择器 在CSS中,最常用的伪类选择器是使用在a(锚)元素上的几种选择器,它们的使用方法如下: a:link{color:#FF0000;text-decoration:none} a:vis ...

随机推荐

  1. java 内部类可以被覆盖吗

    如果创建了一个内部类,然后继承其外围类并重新定义内部类时,"覆盖"内部类就好像是其外围类的一个方法,并不起作用, 这两个内部类是完全独立的两个实体,各自在自己的命名空间内 //: ...

  2. POJ 3376 Finding Palindromes(manacher求前后缀回文串+trie)

    题目链接:http://poj.org/problem?id=3376 题目大意:给你n个字符串,这n个字符串可以两两组合形成n*n个字符串,求这些字符串中有几个是回文串. 解题思路:思路参考了这里: ...

  3. new Function和eval区别

    eval和new Function都可以动态解析和执行字符串.但是它们对解析内容的运行环境判定不同. eval中的代码执行时的作用域为当前作用域.它可以访问到函数中的局部变量. new Functio ...

  4. 函数fgets和fputs、fread和fwrite用法小结(转)

    字符串读写函数fgets和fputs: 1.fgets()函数:原型char *fgets(char *s, int n, FILE *stream);从流中读取n-1(n默认1024)个字符之前,如 ...

  5. LAMP:用yum安装

    LAMP:用yum安装 Table of Contents 1 什么是LAMP 1.1 L 1.2 A 1.3 M 1.4 P 2 什么是yum 3 如何使用yum 3.1 基本的yum命令 3.1. ...

  6. spring-boot分环境打包为war包

    1.启动类修改 @EnableSwagger2 @SpringBootApplication public class CustWebAcApplication extends SpringBootS ...

  7. 湖南联通发福利了C#为你月赚150M流量回家过年不再愁

    回家过年没流量怎么能行,这里教大家一个月赚150流量的方法,哈哈,首先下载联通客户端,本人只有android手机一台,没办法只能用 android的了,里面有一个悦分享,上几张图,图有有我赚的流量. ...

  8. 手动安装pydev

    在网上下载pydev.zip,解压后有两个文件夹,features和plugins.把这两个文件夹复制到eclipse目录下的dropins文件夹下.

  9. bzoj 1232 [Usaco2008Nov]安慰奶牛cheer

    思路:看出跟dfs的顺序有关就很好写了, 对于一棵树来说确定了起点那么访问点的顺序就是dfs序,每个点经过 其度数遍,每条边经过2边, 那么我们将边的权值×2加上两端点的权值跑最小生成树,最后加上一个 ...

  10. 微信小程序official-account组件开发

    今天微信公众平台发了一条消息 扫码打开小程序新增公众号关注组件 官方apihttps://developers.weixin.qq.com/miniprogram/dev/component/offi ...