主程手记系列
文章目录
- 主程手记系列
- C#的List数据结构的扩容原理是什么,有什么优缺点?
- C#的List数据结构的Remove移除原理是什么,时间复杂度是多少?
- C#的List数据结构的Remove移除原理是什么,时间复杂度是多少?
- C#的List数据结构的Insert插入原理是什么,时间复杂度是多少?
- C#的List数据结构的Clear清除原理是什么?
- C#的List数据结构的Contains原理是什么?
- C#的List数据结构的ToArray接口原理是什么,时间复杂度多少?
- C#的List数据结构的Find接口原理是什么?时间复杂度多少?
- C#的List数据结构的Enumerator接口使用有什么缺点?
- C#的List数据结构的Sort接口原理是什么?
- List结构总结
C#的List数据结构的扩容原理是什么,有什么优缺点?
- List的
底层数据结构是数组,容量不够时整个数组会扩充一倍,在声明List时不指定容量则默认容量是4 - 优点是索引方式提取元素很快,缺点是扩容时每次针对数组new操作会造成内存垃圾,给GC带来一些负担
C#的List数据结构的Remove移除原理是什么,时间复杂度是多少?
- 列表从头到尾遍历,找到首次出现的元素索引,然后用Object.Equals方法比较列表中的元素和给定值,然后用RemoveAt函数来移除指定位置的元素;所以原理就是用Array.Copy对数组覆盖
- 复杂度为线性迭代O(n)
C#的List数据结构的Remove移除原理是什么,时间复杂度是多少?
- 列表从头到尾遍历,找到首次出现的元素索引,然后用Object.Equals方法比较列表中的元素和给定值,然后用
RemoveAt函数来移除指定位置的元素;所以原理就是用Array.Copy对数组覆盖 - 复杂度为线性迭代O(n)
C#的List数据结构的Insert插入原理是什么,时间复杂度是多少?
- 和Add接口一样,先检查容量是否足够,不足则扩容;并且插入元素是用复制数组的形式,将数组指定元素后面所有元素向后移动一个位置
- 复杂度为O(K),显然K是需要向后移动的元素数量
C#的List数据结构的Clear清除原理是什么?
- Clear不会删除数组,只将数组中的元素设置为0或NULL,并设置_size为0,虚拟地表明当前容量为0
C#的List数据结构的Contains原理是什么?
- Contains本身作用是确定某元素是否在List中,线性查找比较元素,如果查找的是null,先转化为Object来判断,如果是非null元素则通过Equals判断元素是否相等,无论查找元素是否为null,找到则返回true
csharp
// 如果指定的元素在List中,则Contains返回true
// 它执行线性O(n)搜索。平等是通过调用item.Equals()来确定的
//
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;
}
}
C#的List数据结构的ToArray接口原理是什么,时间复杂度多少?
- 通过创建指定大小的数组,然后将本身数组的内容复制到新数组上完成,但使用过多会造成大量内存分配,在内存上留下很多垃圾
- 显然需要遍历所有元素,所以复杂度为O(n)
csharp
// ToArray返回一个新的Object数组,其中包含List的内容
// 这需要复制列表,这是一个O(n)操作
public T[] ToArray() {
Contract.Ensures(Contract.Result<T[]>() != null);
Contract.Ensures(Contract.Result<T[]>().Length == Count);
T[] array = new T[_size];
Array.Copy(_items, 0, array, 0, _size);
return array;
}
C#的List数据结构的Find接口原理是什么?时间复杂度多少?
- 线性遍历每个元素并且比较,找到就返回
- 时间复杂度为O(n)
csharp
public T Find(Predicate<T>match) {
if( match == null) {
ThrowHelper.ThrowArgumentNullException(ExceptionArgument.match);
}
Contract.EndContractBlock();
for(int i = 0 ; i<_size; i++) {
if(match(_items[i])) {
return _items[i];
}
}
return default(T);
}
C#的List数据结构的Enumerator接口使用有什么缺点?
- 每次获取迭代器时,Enumerator都会被创建出来,大量使用会产生大量垃圾对象,比如用foreach,通过foreach遍历List会增加Enumerator对象,尽管.Net4.0后已修复问题,但仍然不建议使用
csharp
// 返回具有给定删除元素权限的此列表的枚举数
// 如果在进行枚举时对列表进行了修改,
// 则枚举器的MoveNext和GetObject方法将引发异常
//
public Enumerator GetEnumerator() {
return new Enumerator(this);
}
/// 仅供内部使用
IEnumerator<T>IEnumerable<T>.GetEnumerator() {
return new Enumerator(this);
}
System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator() {
return new Enumerator(this);
}
[Serializable]
public struct Enumerator : IEnumerator<T>, System.Collections.IEnumerator
{
private List<T>list;
private int index;
private int version;
private T current;
internal Enumerator(List<T>list) {
this.list = list;
index = 0;
version = list._version;
current = default(T);
}
public void Dispose() {
}
public bool MoveNext() {
List<T>localList = list;
if (version == localList._version&&((uint)index<(uint)localList._size))
{
current = localList._items[index];
index++;
return true;
}
return MoveNextRare();
}
private bool MoveNextRare()
{
if (version != list._version) {
ThrowHelper.ThrowInvalidOperationException(
ExceptionResource.InvalidOperation_EnumFailedVersion);
}
index = list._size + 1;
current = default(T);
return false;
}
public T Current {
get {
return current;
}
}
Object System.Collections.IEnumerator.Current {
get {
if( index == 0 || index == list._size + 1) {
ThrowHelper.ThrowInvalidOperationException(
ExceptionResource.InvalidOperation_EnumOpCantHappen);
}
return Current;
}
}
void System.Collections.IEnumerator.Reset() {
if (version != list._version) {
ThrowHelper.ThrowInvalidOperationException(
ExceptionResource.InvalidOperation_EnumFailedVersion);
}
index = 0;
current = default(T);
}
}
C#的List数据结构的Sort接口原理是什么?
- 使用Array.Sort接口进行排序,而Array.Sort使用快速排序
List结构总结
- List本身效率不是很高,通用性强
- List
内存分配不合理,创建List实例时需要提前声明元素容量,这样可以减少List扩容次数,进而防止List底层的数组被频繁丢弃,进而减少GC的压力 - List本身
线程不安全,没有对多线程加锁或同步,并发情况下无法判断_size++的执行顺序,即多线程下使用List应该加安全机制