讲究地使用 List
本篇旨意在于讨论List的基本用法,不做全面讲解,仅仅涉及构造函数List、Add、RemoveAt
先看看这几个函数的代码
1、构造函数
static readonly T[] _emptyArray = new T[0];
private const int _defaultCapacity = 4; // 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;
} // Constructs a List with a given initial capacity. The list is
// initially empty, but will have room for the given number of elements
// before any reallocations are required.
//
public List(int capacity) {
if (capacity < 0) ThrowHelper.ThrowArgumentOutOfRangeException(ExceptionArgument.capacity, ExceptionResource.ArgumentOutOfRange_NeedNonNegNum);
Contract.EndContractBlock(); if (capacity == 0)
_items = _emptyArray;
else
_items = new T[capacity];
} // Constructs a List, copying the contents of the given collection. The
// size and capacity of the new list will both be equal to the size of the
// given collection.
//
public List(IEnumerable<T> collection) {
if (collection==null)
ThrowHelper.ThrowArgumentNullException(ExceptionArgument.collection);
Contract.EndContractBlock(); ICollection<T> c = collection as ICollection<T>;
if( c != null) {
int count = c.Count;
if (count == 0)
{
_items = _emptyArray;
}
else {
_items = new T[count];
c.CopyTo(_items, 0);
_size = count;
}
}
else {
_size = 0;
_items = _emptyArray;
// This enumerable could be empty. Let Add allocate a new array, if needed.
// Note it will also go to _defaultCapacity first, not 1, then 2, etc. using(IEnumerator<T> en = collection.GetEnumerator()) {
while(en.MoveNext()) {
Add(en.Current);
}
}
}
}
2、Add
public void Add(T item) {
if (_size == _items.Length) EnsureCapacity(_size + 1);
_items[_size++] = item;
_version++;
}
private void EnsureCapacity(int min) {
if (_items.Length < min) {
int newCapacity = _items.Length == 0? _defaultCapacity : _items.Length * 2;
// Allow the list to grow to maximum possible capacity (~2G elements) before encountering overflow.
// Note that this check works even when _items.Length overflowed thanks to the (uint) cast
if ((uint)newCapacity > Array.MaxArrayLength) newCapacity = Array.MaxArrayLength;
if (newCapacity < min) newCapacity = min;
Capacity = newCapacity;
}
}
public int Capacity {
get {
Contract.Ensures(Contract.Result<int>() >= 0);
return _items.Length;
}
set {
if (value < _size) {
ThrowHelper.ThrowArgumentOutOfRangeException(ExceptionArgument.value, ExceptionResource.ArgumentOutOfRange_SmallCapacity);
}
Contract.EndContractBlock();
if (value != _items.Length) {
if (value > 0) {
T[] newItems = new T[value];
if (_size > 0) {
Array.Copy(_items, 0, newItems, 0, _size);
}
_items = newItems;
}
else {
_items = _emptyArray;
}
}
}
}
3、RemoveAt
public void RemoveAt(int index) {
if ((uint)index >= (uint)_size) {
ThrowHelper.ThrowArgumentOutOfRangeException();
}
Contract.EndContractBlock();
_size--;
if (index < _size) {
Array.Copy(_items, index + , _items, index, _size - index);
}
_items[_size] = default(T);
_version++;
}
上面是.Net4.7.2的代码
private List<CDBot> cdObjList = new List<CDBot>(); public void RemoveByItem(CDBot item)
{
if (item.index != -)
{
if (cdObjList.Count > item.index)
{
cdObjList.RemoveAt(item.index);
for (int i = item.index; i < cdObjList.Count; i++)
{
--cdObjList[i].index;
}
item.index = -;
}
}
}
这段代码是段管理计时器的代码,CDBot.index是其在 cdObjList中的下标利于管理。经过观察,cdObjList在平稳运行时,Cout稳定在100左右。
构造函数的源码告诉我们,调用一个不带参数的List构造函数,生成的是一个空的数组,接下来调用Add后通过更新Capacity重新new一个数组实现容量的自增长,默认长度为4,两倍的速度增长,
那么cdObjList的实际容量变化将会是0、4、8、16、32、64、128。重新生成数组意味着之前的需要被丢弃,C#的堆内存是GC制而非软件工程师自行掌控,那么之前被丢弃的内容就成为了GC的对象。
针对上面我做出如下修改
private List<CDBot> cdObjList = new List<CDBot>();
private const int CDObjInvalidIndex = -; public void RemoveByItem(CDBot item)
{
if (item.index != CDObjInvalidIndex && cdObjList.Count > item.index)
{
cdObjList.RemoveAt(item.index);
for (int i = item.index; i < cdObjList.Count; i++)
{
--cdObjList[i].index;
}
item.index = CDObjInvalidIndex;
}
}
给定一个初始容量,减少List自增长造成的数组更换,但这样做也有个问题,也许随着项目的开发,List的稳定容量将会发生变化,以我目前的想法可以封装一下List的构造、Add,对Cout进行监测,在Debug模式下提醒工程师重新设置List的初始容量。
List的实现是用数组,那么删除中间的元素需要将后面的元素前移。测试发现 cdObjList 执行 Remove 时元素命中范围有60%在20~70之间,对此作出如下修改
private List<CDBot> cdObjList = new List<CDBot>();
private const int CDObjInvalidIndex = -; public void RemoveByItem(CDBot item)
{
if (item.index != CDObjInvalidIndex && cdObjList.Count > item.index)
{
cdObjList[item.index] = cdObjList[cdObjList.Count - ];
cdObjList[item.index].index = item.index;
item.index = CDObjInvalidIndex;
cdObjList.RemoveAt(cdObjList.Count - );
}
}
将末尾的元素覆盖需要删除的元素,改为删除最后一个元素从而达到避免元素前移带来的消耗,但此做法具有List的元素非排序状态的局限性。
private void DealLevelUpMaterials(uint level, int type)
{
var levelUpCfg = materialCfg.GetConfigureData(level, type);
List<MaterialData> NeedMaterials = levelUpCfg.NeedMaterials;
for (int i = ; i < NeedMaterials.Count; ++i)
{
List<PBDelItemata> itemList;
var material = NeedMaterials[i];
if (BagCtr.Model.TryGetItemSNList((uint)material.id, (uint)material.count, out itemList))
{
//do something
}
}
} public bool TryGetItemSNList(uint itemID, uint needCount, out List<PBDelItemata> itemList)
{
itemList = new List<PBDelItemata>();
if (GetItemCount((uint)itemID) == )
{
return false;
} List<BaseItem> items = GetItemById(itemID);
while (needCount > )
{
for (int i = ; i < items.Count; i++)
{
//do something
//itemList.Add(delItem);
}
}
return true;
}
上面的这段代码 DealLevelUpMaterials 中的 itemList 会在 TryGetItemSNList 中分配实体,其分配次数取决于 for ,联系上下文,这个List可复用 如下修改
private void DealLevelUpMaterials(uint level, int type)
{
var levelUpCfg = materialCfg.GetConfigureData(level, type);
List<MaterialData> NeedMaterials = levelUpCfg.NeedMaterials;
List<PBDelItemata> itemList = new List<PBDelItemata>();
for (int i = ; i < NeedMaterials.Count; ++i)
{
var material = NeedMaterials[i];
itemList.Clear();
if (BagCtr.Model.TryGetItemSNList((uint)material.id, (uint)material.count, ref itemList))
{
//do something
}
}
} public bool TryGetItemSNList(uint itemID, uint needCount, ref List<PBDelItemata> itemList)
{
if (GetItemCount((uint)itemID) == )
{
return false;
} List<BaseItem> items = GetItemById(itemID);
while (needCount > )
{
for (int i = ; i < items.Count; i++)
{
//do something
//itemList.Add(delItem);
}
}
return true;
}
如有错误欢迎指正
讲究地使用 List的更多相关文章
- SQLite 入门教程(四)增删改查,有讲究 (转)
转于: SQLite 入门教程(四)增删改查,有讲究 一.插入数据 INSERT INTO 表(列...) VALUES(值...) 根据前面几篇的内容,我们可以很轻送的创建一个数据表,并向其中插入一 ...
- 讲究门面的Request
为什么说Request讲究门面?注意这里所说的门面并非我们常理解的外表的意思,其实是说它使用了门面设计模式,门面的使用主要用于数据安全的考虑.一个大的系统体系的多个子系统之间涉及交互通信.一个系统中的 ...
- 自己定义的Excetpion继承哪个异常有什么讲究?[待解答]
try catch的地方需要用到一个自定义的DBException,如下: 于是我就自定义了一个DBException,继承Excetpion,以父类Exception构造器创建构造器: DBExce ...
- KCP TCP是为流量设计的(每秒内可以传输多少KB的数据),讲究的是充分利用带宽。而KCP是为流速设计的(单个数据包从一端发送到一端需要多少时间)
http://www.skywind.me/blog/archives/1048 KCP是一个快速可靠协议,能以比 TCP浪费10%-20%的带宽的代价,换取平均延迟降低 30%-40%,且最大延迟降 ...
- C# 程序的关闭 讲究解释
程序的关闭是很讲究的,处理的不好的话,将软件连续开启和关闭,当数次后在启动软件后程序会崩溃.或者程序退出很慢.细节决定成败,一款好的软件应该从各方面都要做严格地反复地推敲,力争做到无可挑剔. 有 ...
- 咦,Java拆分个字符串都这么讲究
提到 Java 拆分字符串,我猜你十有八九会撂下一句狠话,"这有什么难的,直接上 String 类的 split() 方法不就拉到了!"假如你真的这么觉得,那可要注意了,事情远没这 ...
- CocoaPods Podfile 文件写法有讲究
最近做到一些项目想到用 OC/Swift混编的问题.为了搞懂 bridge header 这个文件是咋个情况.却一致报错.最后不知不觉发现问题问题居然出在 Podfile 上. 开始我是从 IT 江湖 ...
- SQLite 入门教程(四)增删改查,有讲究
增删改查操作,其中增删改操作被称为数据操作语言 DML,相对来说简单一点. 查操作相对来说复杂一点,涉及到很多子句,所以这篇先讲增删改操作,以例子为主,后面再讲查操作. 一.插入数据 INSERT I ...
- XP里面其实也讲究admin的执行权限
错误的方法:比如说,当前登录帐号cliff是管理员,此时直接运行cmd,输入: net user administrator 123 结果说这个用户找不到. --------------------- ...
- 在CMainFrame里使用定时器是有讲究的
设置定时器函数:SetTimer 单位毫秒 销毁定时器函数:KillTimer 消息:WM_TIMER 注意事项: (1)不要在构造函数里设置定时器. (2)不要在析构函数里销毁定时器. 原因:构造函 ...
随机推荐
- C# DataTable扩展方法
在日常搬砖中,总结了一些简单的扩展方法. public static bool IsNullOrEmpty(this DataTable dt) { ; } public static bool Is ...
- adjtimex和时钟的几个概念tick,freq,ppm,jiffies
adjtimex使用 今天遇到一个ntp的同步问题.服务器上配置好了ntpd,在启动前也手动进行过同步,但是过段时间ntpq查询发现服务器即便能选出同步服务器,但是系统的时间偏差越来越大. 服务器上实 ...
- H5 web存储
H5提供了两种在客户端存储数据的方式:localStorage 持久化的本地存储(浏览器关闭重新打开数据依然存在)sessionStorage 针对一个session的本地存储之前这些都是由cooki ...
- Jupyter Notebook 下安装 PHP 内核
我最近被强烈安利了 Jupyter Notebook 这个交互式笔记本.然后试用了它自带的 Python 内核后,这个应用整体给我的感觉很不错,就去搜索了下它所支持的其它内核 Jupyter Kern ...
- 数据类型与变量(Python学习笔记01)
数据类型与变量 Python 中的主要数据类型有 int(整数)/float(浮点数).字符串.布尔值.None.列表.元组.字典.集合等. None 每个语言都有一个专门的词来表示空,例如 Java ...
- scrapy框架的日志等级和请求传参, 优化效率
目录 scrapy框架的日志等级和请求传参, 优化效率 Scrapy的日志等级 请求传参 如何提高scripy的爬取效率 scrapy框架的日志等级和请求传参, 优化效率 Scrapy的日志等级 在使 ...
- Spring MVC 一次简单的 CRUD
基本环境搭建 1.数据库 和 实体类 的名字相同,实体类 属性名即 数据库 字段名. 2.创建 实体类 对应 dao 类,持久层框架 mybatis 正处于学习中,这里就用原始的 jdbc 操作了. ...
- Xmemcached使用之与Spring整合
转自:http://hi.baidu.com/tjbaso/item/22f3c32b062ebefb50fd87b8 1 简介Xmemcached是一个高性能的基于java nio的memcache ...
- 菜鸟nginx源代码剖析数据结构篇(六) 哈希表 ngx_hash_t(上)
菜鸟nginx源代码剖析数据结构篇(六) 哈希表 ngx_hash_t(上) Author:Echo Chen(陈斌) Email:chenb19870707@gmail.com Blog:Blog. ...
- Extjs TabPanel页签转换事件
listeners : { tabchange : function(tp, p) { var allmapDIV = document.getElementById("allmap&quo ...