剖析.NET Framework源码—数据结构List<T> (二)
接着,我们看看List中对数据进行操作的方法:
(2)Add:将对象添加到 List<T>的结尾处。
public void Add(T item)
{
if (_size == _items.Length) EnsureCapacity(_size + 1);
_items[_size++] = item;
_version++;
}
这个方法有两个要点:
1. 当前List已满(_size==_items.Length)时,首先需要进行扩容!
2. 往List内部数组_items中添加新元素的操作_items[_size++] = item存在 多线程并发访问的问题!
(3)AddRange:将指定集合的元素添加到 List的末尾
public void AddRange(IEnumerable<T> collection)
{
InsertRange(_size, collection);
}
这个方法对collection不做任何处理,直接调用InsertRange方法。
(4)InsertRange : 将集合中的所有元素插入List的指定索引处。
// Inserts the elements of the given collection at a given index. If
// required, the capacity of the list is increased to twice the previous
// capacity or the new size, whichever is larger. Ranges may be added
// to the end of the list by setting index to the List's size.
public void InsertRange(int index, IEnumerable<T> collection)
{
if (collection == null)
{
throw new ArgumentNullException();
}
if ((uint)index > (uint)_size)
{
throw new ArgumentOutOfRangeException();
}
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++;
}
这个方法很长,不过其思路是很清晰的。首先进行两次参数合法性验证,如果某次验证失败了,就直接抛个异常出去。注意第二个合法性检查,它检查的是要插入collection元素到List的指定索引位置index,其合法区间是[0 , _size]!
接着,会把传入的collection尝试转 换为ICollection<T>接口,如果转换成功,说明collection实现了ICollection接口,那么就执行if语句块;否则,执行else语句块。
If语句块的基本思路是:首先获得collection中元素的个数count,接着调用EnsureCapacity(_size + count)以确定是否需要扩容,然后根据所要插入的索引位置index在内部数组_items中腾出count个位置以便存储collection中的元素。紧接着创建一个itemsToInsert辅助数组用于存放collection中的所有元素,然后把itemsToInsert中所有的元素(count个)拷贝到List的内部数组_items中早已腾出的空间中(count个位置)。最后再把_size的值加上count。注意,在if语句块中我们还有一个地方未涉及到,就是if (this == c)大括号中的代码,这是一种特殊情况,表示要插入的collection就是当前这个List,分析思路类似!
else语句块的思路是:利用collection的迭代器,将collection中的元素一个一个插入到_items数组的指定索引index位置后面!注意这里会调用到Insert方法,这也是List的核心方法之一。
(5)Insert(int index, T item):将元素插入List的指定索引处
public void Insert(int index, T item)
{
// index的合法区间是[0 , _size]
if ((uint)index > (uint)_size)
{
throw new ArgumentOutOfRangeException();
}
if (_size == _items.Length) EnsureCapacity(_size + 1);
if (index < _size)
{
// index处及后面所有元素后移一位,腾出index这个位置!
Array.Copy(_items, index, _items, index + 1, _size - index);
}
//要插入的元素就放在指定的index位置处!
_items[index] = item;
_size++;
_version++;
}
(6)CopyTo :三个重载,将 List或它的一部分复制到一个数组中
//参数array : 目标数组
//参数arrayIndex : 目标数组开始放置处的索引
public void CopyTo(T[] array, int arrayIndex)
{
Array.Copy(_items, 0, array, arrayIndex, _size);
}
//参数index : 源List中复制开始位置的从零开始的索引
//参数count : 要复制的元素个数
public void CopyTo(int index, T[] array, int arrayIndex, int count)
{
if (_size - index < count)
{
throw new ArgumentException();
}
Array.Copy(_items, index, array, arrayIndex, count);
}
//将整个List复制到类型兼容的一维数组中,从目标数组的0索引开始放置
public void CopyTo(T[] array)
{
CopyTo(array, 0);
}
(7)Contains(T item) : 确定某元素是否在List中
public bool Contains(T item)
{
if ((Object)item == null)
{
for (int i = 0; i < _size; i++)
{
if ((Object)_items[i] == null)
return true;
}
return false;
}
else
{
EqualityComparer<T> c = EqualityComparer<T>.Default;
for (int i = 0; i < _size; i++)
{
if (c.Equals(_items[i], item)) return true;
}
return false;
}
}
该方法有两条执行路径。当所要查找的元素为null时执行if语句块;否则执行else语句块。
两条执行路径的基本思路都是一样的:遍历List内部数组,如果找到了所要查找的元素,则返回True!注意else语句块,首先获得了一个泛型T的相等性比较器EqualityComparer<T>,然后通过相等性比较器的Equals方法比较两个对象是否相等!