本篇旨意在于讨论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的更多相关文章

  1. SQLite 入门教程(四)增删改查,有讲究 (转)

    转于: SQLite 入门教程(四)增删改查,有讲究 一.插入数据 INSERT INTO 表(列...) VALUES(值...) 根据前面几篇的内容,我们可以很轻送的创建一个数据表,并向其中插入一 ...

  2. 讲究门面的Request

    为什么说Request讲究门面?注意这里所说的门面并非我们常理解的外表的意思,其实是说它使用了门面设计模式,门面的使用主要用于数据安全的考虑.一个大的系统体系的多个子系统之间涉及交互通信.一个系统中的 ...

  3. 自己定义的Excetpion继承哪个异常有什么讲究?[待解答]

    try catch的地方需要用到一个自定义的DBException,如下: 于是我就自定义了一个DBException,继承Excetpion,以父类Exception构造器创建构造器: DBExce ...

  4. KCP TCP是为流量设计的(每秒内可以传输多少KB的数据),讲究的是充分利用带宽。而KCP是为流速设计的(单个数据包从一端发送到一端需要多少时间)

    http://www.skywind.me/blog/archives/1048 KCP是一个快速可靠协议,能以比 TCP浪费10%-20%的带宽的代价,换取平均延迟降低 30%-40%,且最大延迟降 ...

  5. C# 程序的关闭 讲究解释

    程序的关闭是很讲究的,处理的不好的话,将软件连续开启和关闭,当数次后在启动软件后程序会崩溃.或者程序退出很慢.细节决定成败,一款好的软件应该从各方面都要做严格地反复地推敲,力争做到无可挑剔.    有 ...

  6. 咦,Java拆分个字符串都这么讲究

    提到 Java 拆分字符串,我猜你十有八九会撂下一句狠话,"这有什么难的,直接上 String 类的 split() 方法不就拉到了!"假如你真的这么觉得,那可要注意了,事情远没这 ...

  7. CocoaPods Podfile 文件写法有讲究

    最近做到一些项目想到用 OC/Swift混编的问题.为了搞懂 bridge header 这个文件是咋个情况.却一致报错.最后不知不觉发现问题问题居然出在 Podfile 上. 开始我是从 IT 江湖 ...

  8. SQLite 入门教程(四)增删改查,有讲究

    增删改查操作,其中增删改操作被称为数据操作语言 DML,相对来说简单一点. 查操作相对来说复杂一点,涉及到很多子句,所以这篇先讲增删改操作,以例子为主,后面再讲查操作. 一.插入数据 INSERT I ...

  9. XP里面其实也讲究admin的执行权限

    错误的方法:比如说,当前登录帐号cliff是管理员,此时直接运行cmd,输入: net user administrator 123 结果说这个用户找不到. --------------------- ...

  10. 在CMainFrame里使用定时器是有讲究的

    设置定时器函数:SetTimer 单位毫秒 销毁定时器函数:KillTimer 消息:WM_TIMER 注意事项: (1)不要在构造函数里设置定时器. (2)不要在析构函数里销毁定时器. 原因:构造函 ...

随机推荐

  1. oracle数据库视图,序列,索引的sql语句查看

    1.视图:相当于表,可以用select * from tab;查看所有表和视图: 2.序列和索引可以利用select * from user_indexes 或者user_sequences;进行查看 ...

  2. 破解js中的this指向

    首先必须要说的是,this的指向在函数定义的时候是确定不了的,只有函数执行的时候才能确定this到底指向谁,实际上this的最终指向的是那个调用它的对象(这句话有些问题,后面会解释为什么会有问题,虽然 ...

  3. NOIP2018 滚粗记

    Day -2  上午,大家都在复习各种模板,zhx总结了足足67个模板(杨辉三角也算模板???),lgl死磕FFT发现cos和sin打反了,我也是复习板子和以前做过的题,几乎没有人颓. 接着jdr,l ...

  4. ELK6 收集不同来源的日志并做区分

    https://blog.csdn.net/u010871982/article/details/79035317 使用filebeat替代logstash进行日志采集 https://blog.cs ...

  5. 举枪消灭"烂代码"的实战案例

    前言 之前我写过一篇如何少写PHP "烂"代码 https://segmentfault.com/a/11...感觉很多新人对此不太理解.今天以打卡功能为例,去讲解其中的奥秘.那篇 ...

  6. Contest Round #451 (Div. 2)F/Problemset 898F Restoring the Expression

    题意: 有一个a+b=c的等式,去掉两个符号,把三个数连在一起得到一个数 给出这个数,要求还原等式,length <= 1e6 三个数不能含有前导0,保证有解 解法: 铁头过题法,分类然后各种判 ...

  7. Alliances

    树国是一个有n个城市的国家,城市编号为1∼n.连接这些城市的道路网络形如一棵树, 即任意两个城市之间有恰好一条路径.城市中有k个帮派,编号为1∼k.每个帮派会占据一些城市,以进行非法交易.有时帮派之间 ...

  8. Linux轻松一下——cowsay命令,让动物说话

    Linux动物说话命令 使用方法 安装命令:sudo apt-get install cowsay 使用命令:cowsay hello 查看可选动物 cowsay -l 使用其他动物 cowsay - ...

  9. 在Ubuntu 14.04 上安装 FTP 服务

    1. sudo apt-get update 2. sudo apt-get install vsftpd 3. adduser sammy Assign a password when prompt ...

  10. 多个机器获取微信access-token导致的有效性问题

    多个机器获取微信access-token导致的有效性问题 单个机器获取的access-token,只有最后一个是有效的: 多个机器各自获取自己的access-token,都是各自有效的: 在服务器和本 ...