本篇旨意在于讨论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. 27.6 Parallel的静态For,Foreach和Invoke方法

    static void Main(string[] args) { //for (int i = 0; i < 10000; i++) // DoWork(i); //Parallel.For( ...

  2. 计蒜客 劫富济贫 (Trie树)

    链接 : Here! 思路 : Trie树裸题, 由开始给出的名字建一棵字典树, 然后每次查询一下抢♂劫的人名是否在字典树中, 复杂度也不清楚是多少, 反正是没给出 $M$ 的范围, 开始时用 $ha ...

  3. 更简单高效的HTML数据提取-Xpath

    XPath 是一门在 XML 文档中查找信息的语言.XPath 用于在 XML 文档中通过元素和属性进行导航. 相比于BeautifulSoup,Xpath在提取数据时会更加的方便. 安装 在Pyth ...

  4. Beetl学习总结(4)——Web集成

    4.1. Web提供的全局变量 Web集成模块向模板提供web标准的变量,做如下说明 request 中的所有attribute.在模板中可以直接通过attribute name 来引用,如在cont ...

  5. Monthly Expense POJ 二分

    Description Farmer John is an astounding accounting wizard and has realized he might run out of mone ...

  6. 络谷 P1363 幻想迷宫

    P1363 幻想迷宫 题目描述 背景 Background (喵星人LHX和WD同心协力击退了汪星人的入侵,不幸的是,汪星人撤退之前给它们制造了一片幻象迷宫.) WD:呜呜,肿么办啊…… LHX:mo ...

  7. hdu 1874 畅通project续

    最短路问题,尽管a!=b,可是同一条路測评数据会给你非常多个.因此在读入的时候要去最短的那条路存起来.........见了鬼了.坑爹 #include<iostream> #include ...

  8. Codeforces Round #306 (Div. 2) A

    题意 给一个字符串(长度<=10^5).问当中有没有一个"BA"和一个"AB"呢?假设都有而且它们不反复(即ABA不算),输出YES.否则输出NO. 思路 ...

  9. Ubuntu下终端Vim编写C语言程序 AAAAA

    我是开虚拟机下的Ubuntu,装双系统又卸了,Ubuntu默认是不包含编辑器vim和编译器gcc.如果你是刚安装好的Ubuntu电脑,下面我们将来实现自己的第一个程序. 1.准备工作 首先进入root ...

  10. P1228 地毯填补问题(分治)

    P1228 地毯填补问题(分治) 题目描述 相传在一个古老的阿拉伯国家里,有一座宫殿.宫殿里有个四四方方的格子迷宫,国王选择驸马的方法非常特殊,也非常简单:公主就站在其中一个方格子上,只要谁能用地毯将 ...