讲究地使用 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)不要在析构函数里销毁定时器. 原因:构造函 ...
随机推荐
- API开发管理平台eoLinker AMS 4.1版本发布:加入聚合空间,发布AMS专业版等
eoLinker AMS是集API文档管理.API自动化测试.开发协作三位一体的综合API开发管理平台,是中国最大的在线API管理平台. eoLinker AMS 4.1更新内容: 1.新增" ...
- React 服务器渲染原理解析与实践
第1章 服务器端渲染基础本章主要讲解客户端与服务器端渲染的概念,分析客户端渲染和服务器端渲染的利弊,带大家对服务器端渲染有一个粗浅认识. 1-1 课程导学1-2 什么是服务器端渲染1-3 什么是客户端 ...
- css 字体单位之间的区分以及字体响应式实现
问题场景: 在实现响应式布局的过程中,如何设置字体大小在不同的视窗尺寸以及不同的移动设备的可读性? 需要了解的有: 1.px,em,pt之间的换算关系 1em = 16px 1px = 1/16 e ...
- Ubuntu网卡设置:配置/etc/netplan
对于Ubuntu1804版本,经过测试如下配置可以设置静态IP地址: Google@ubuntu:~$ cat /etc/netplan/01-netcfg.yaml network: etherne ...
- 到Oracle官网下载 Oracle11 G 数据可和客户端操作
1.准备一个Oracle的官网账号 用户名:541509124@qq.com 密码:LR4ever.1314 2.在搜索框中输入Oracle 11 G 3.点击Database Downloadds ...
- navicate远程访问ubuntu上的mysql数据库
安装mysql 首先检查系统中是否已经安装了MySQL,在终端里面输入: sudo netstat -tap | grep mysql 如上所示就是正确安装并启动,启动命令为 sudo /etc/in ...
- max_element()与min_element()
#include<iostream>#include<algorithm>using namespace std;bool cmp(int i,int j){ return i ...
- Pycharm 的基本操作
下载:https://www.jetbrains.com/pycharm/ 安装:随意安装在那个目录都可以 注册:可以采用 激活码 或者激活服务器,并对应在选项下面填入激活码或者激活服务器URL即可. ...
- PatentTips - Power management implementation in an optical link
BACKGROUND INFORMATION Embodiments of the present invention are directed to optical links and, more ...
- TASKLIST 显示计算机上的所有进程
Tasklist"是 winxp/win2003/vista/win7/win8下的命令,用来显示运行在本地或远程计算机上的所有进程,带有多个执行参数. 使用格式 tasklist [/s ...