线性表(五):.NET Framework泛型List类
2012-03-14 18:54:23一、采用数组保存数据
部分私有成员如下:
public class List<T> : IList<T>, System.Collections.IList
{
private const int _defaultCapacity = 4; //初始list大小
private T[] _items; //list数据项保存的数组
private int _size; //数据项的实际个数
private int _version; //数据项被修改过的次数,主要用户枚举过程中对于数据的修改
static T[] _emptyArray = new T[0];
//................其他部分
}
数据项查找复杂度为O(n);
public int IndexOf(T item)
{
return Array.IndexOf(_items, item, 0, _size);
}
下标查找复杂度为O(1);
二、当数据空间不够时,扩大1倍空间
带来O(n)的数据复制开销。最坏可能带来sizeof(T)*(n-1)的空间浪费(可以使用TrimExcess来缩紧)。
//获取与设置保存数据项数组的长度,注意在设置长度时所带来的开销
public int Capacity
{
get { return _items.Length; }
set
{
if (value != _items.Length)
{
if (value < _size)
{
ThrowHelper.ThrowArgumentOutOfRangeException(ExceptionArgument.value, ExceptionResource.ArgumentOutOfRange_SmallCapacity);
}
if (value > 0)
{
//注意_size个元素复制开销,与原有对象丢弃后可引起cc启动的开销
T[] newItems = new T[value];
if (_size > 0)
{
Array.Copy(_items, 0, newItems, 0, _size);
}
_items = newItems;
}
else
{
_items = _emptyArray;
}
}
}
}
//当期望数组容量最小值min大于等于预有数组的容量时,不需任何操作
//否则数组的容量将至少扩充为当前容量的2倍,实际数组要么当前容量的2倍,要么为min
//另外,如果当前数组长度为0,数组容量要么为_defaultCapacity,要么为min,但不会小于_defaultCapacity
//注意,数组扩充时会产生复制和垃圾对象的开销
private void EnsureCapacity(int min)
{
if (_items.Length < min)
{
int newCapacity = _items.Length == 0 ? _defaultCapacity : _items.Length * 2;
if (newCapacity < min) newCapacity = min;
Capacity = newCapacity;
}
}
//只有当数据项的个数小于数据实际长度的0.9倍时,系统才会收索数组
//在收索过程中会创建新的缓冲区数据,涉及到_size个数据项的复制,以及可能会引起cc启动的开销
public void TrimExcess()
{
int threshold = (int)(((double)_items.Length) * 0.9);
if (_size < threshold)
{
Capacity = _size;
}
}
三、插入数据时最坏可能会带来n-1次数据复制
小心InsertRange里面的效率陷阱(对于非IColletion
//注意由于空间不足引起的开销,该方法非线性安全
public void Add(T item)
{
if (_size == _items.Length) EnsureCapacity(_size + 1);
_items[_size++] = item;
_version++;
}
public void AddRange(IEnumerable<T> collection)
{
InsertRange(_size, collection);
}
//注意在插于时会产生(_size-index)次数据复制的开销
//当插于的数据使得原有缓冲空间不足时,会产生(2*_size-index)次复制的开销,同时引起cc启动开销
public void Insert(int index, T item)
{
if ((uint)index > (uint)_size)
{
ThrowHelper.ThrowArgumentOutOfRangeException(ExceptionArgument.index, ExceptionResource.ArgumentOutOfRange_ListInsert);
}
if (_size == _items.Length) EnsureCapacity(_size + 1);
if (index < _size)
{
Array.Copy(_items, index, _items, index + 1, _size - index);
}
_items[index] = item;
_size++;
_version++;
}
//count为collection中包含元素的数量
//在当数据项容量满足插入后容量时
//1.当collection为System.Collection.Generic.ICollection<T>类型,且Collection不指向自己时
//该操作会涉及到(_size-index+2*count)次数据复制操作
//2.当collection为System.Collection.Generic.ICollection<T>类型,且Collection指向自己时
//该操作会涉及到(2*_size-index)次数据复制操作
//3.当collection不是System.Collection.Generic.ICollection<T>类型,该操作循环调用count次Insert函数
//分析情况1与insert操作之间复杂度的比较
//在这种情况下如果使用insert方式实现相同操作,需要(count*(_size-index))次操作
//要求(_size-index+2*count)>(count*(_size-index))
//有2count>(count-1)*(_size-index)
//当count很多时,及时为count约等于count-1,因此有(_size-index)<2,即index>_size-2;
//由于必然有index<_size,所以只有到index=_size-1时该表达式成了,除此之外都不会有InsertRange效率差于多次使用insert方法;
//对于情况2,略
//情况3,实际等于count次调用insert方法
//当数据容量不满足插于后容量后
//1.当collection为System.Collection.Generic.ICollection<T>类型,且Collection不指向自己时
//该操作会涉及到(2*_size-index+2*count)次数据复制操作
//2.当collection为System.Collection.Generic.ICollection<T>类型,且Collection指向自己时
//该操作会涉及到(3*_size-index次数据复制操作
//结论:对于大量差于数据时,尽量使用InsertRange方法,而不是Insert
//另外尽量保证插于的数据为System.Collection.Generic.ICollection<T>类型
public void InsertRange(int index, IEnumerable<T> collection)
{
if (collection == null)
{
ThrowHelper.ThrowArgumentNullException(ExceptionArgument.collection);
}
if ((uint)index > (uint)_size)
{
ThrowHelper.ThrowArgumentOutOfRangeException(ExceptionArgument.index, ExceptionResource.ArgumentOutOfRange_Index);
}
ICollection<T> c = collection as ICollection<T>;
if (c != null)
{
int count = c.Count;
if (count > 0)
{
EnsureCapacity(_size + count);
if (index < _size)
{
Array.Copy(_items, index, _items, index + count, _size - index);
}
if (this == c)
{
Array.Copy(_items, 0, _items, index, index);
Array.Copy(_items, index + count, _items, index * 2, _size - index);
}
else
{
T[] itemsToInsert = new T[count];
c.CopyTo(itemsToInsert, 0);
itemsToInsert.CopyTo(_items, index);
}
_size += count;
}
}
else
{
using (IEnumerator<T> en = collection.GetEnumerator())
{
while (en.MoveNext())
{
Insert(index++, en.Current);
}
}
}
_version++;
}
四、当删除数据时最坏可能带来n-1次数据复制
RemoveAt、RemoveRange、RemoveAll(Predict
public bool Remove(T item)
{
int index = IndexOf(item);
if (index >= 0)
{
RemoveAt(index);
return true;
}
return false;
}
//该方法时间复杂度为O(n),并且不会涉及到创建新的缓冲区和大量数据项在缓冲区的复制操作
public int RemoveAll(Predicate<T> match)
{
if (match == null)
{
ThrowHelper.ThrowArgumentNullException(ExceptionArgument.match);
}
int freeIndex = 0;
while (freeIndex < _size && !match(_items[freeIndex])) freeIndex++;
if (freeIndex >= _size) return 0;
int current = freeIndex + 1;
while (current < _size)
{
while (current < _size && match(_items[current])) current++;
if (current < _size)
{
_items[freeIndex++] = _items[current++];
}
}
Array.Clear(_items, freeIndex, _size - freeIndex);
int result = _size - freeIndex;
_size = freeIndex;
_version++;
return result;
}
//该操作涉及到(_size-index)次数据移动开销
//注意!数据删除后,保存数据的数据容量并没有缩减,如果不对数据的数据容量进行缩减,可能出现数组中大量单元空闲,进而对内存的使用产生浪费
public void RemoveAt(int index)
{
if ((uint)index >= (uint)_size)
{
ThrowHelper.ThrowArgumentOutOfRangeException();
}
_size--;
if (index < _size)
{
Array.Copy(_items, index + 1, _items, index, _size - index);
}
_items[_size] = default(T);
_version++;
}
//该操作涉及到(_size-index)次数据移动开销
//对于多个数据的删除,单次调用RemoveRange优于count次调用RemoveAt
//注意!数据删除后,保存数据的数据容量并没有缩减,如果不对数据的数据容量进行缩减,可能出现数组中大量单元空闲,进而对内存的使用产生浪费
public void RemoveRange(int index, int count)
{
if (index < 0 || count < 0)
{
ThrowHelper.ThrowArgumentOutOfRangeException((index < 0 ? ExceptionArgument.index : ExceptionArgument.count), ExceptionResource.ArgumentOutOfRange_NeedNonNegNum);
}
if (_size - index < count)
ThrowHelper.ThrowArgumentException(ExceptionResource.Argument_InvalidOffLen);
if (count > 0)
{
int i = _size;
_size -= count;
if (index < _size)
{
Array.Copy(_items, index + count, _items, index, _size - index);
}
Array.Clear(_items, _size, count);
_version++;
}
}
五、提供QuickSort排序和二分查找方法