读书笔记——U3D高级编程:主程手记——第二章2.2List底层源码

主程手记系列

  1. 主程手记------第二章2.1读书笔记------Unity编译与GC

文章目录


C#的List数据结构的扩容原理是什么,有什么优缺点?

  1. List的底层数据结构是数组,容量不够时整个数组会扩充一倍,在声明List时不指定容量则默认容量是4
  2. 优点是索引方式提取元素很快,缺点是扩容时每次针对数组new操作会造成内存垃圾,给GC带来一些负担

C#的List数据结构的Remove移除原理是什么,时间复杂度是多少?

  1. 列表从头到尾遍历,找到首次出现的元素索引,然后用Object.Equals方法比较列表中的元素和给定值,然后用RemoveAt函数来移除指定位置的元素;所以原理就是用Array.Copy对数组覆盖
  2. 复杂度为线性迭代O(n)

C#的List数据结构的Remove移除原理是什么,时间复杂度是多少?

  1. 列表从头到尾遍历,找到首次出现的元素索引,然后用Object.Equals方法比较列表中的元素和给定值,然后用RemoveAt函数来移除指定位置的元素;所以原理就是用Array.Copy对数组覆盖
  2. 复杂度为线性迭代O(n)

C#的List数据结构的Insert插入原理是什么,时间复杂度是多少?

  1. 和Add接口一样,先检查容量是否足够,不足则扩容;并且插入元素是用复制数组的形式,将数组指定元素后面所有元素向后移动一个位置
  2. 复杂度为O(K),显然K是需要向后移动的元素数量

C#的List数据结构的Clear清除原理是什么?

  1. Clear不会删除数组,只将数组中的元素设置为0或NULL,并设置_size为0,虚拟地表明当前容量为0

C#的List数据结构的Contains原理是什么?

  1. 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接口原理是什么,时间复杂度多少?

  1. 通过创建指定大小的数组,然后将本身数组的内容复制到新数组上完成,但使用过多会造成大量内存分配,在内存上留下很多垃圾
  2. 显然需要遍历所有元素,所以复杂度为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接口原理是什么?时间复杂度多少?

  1. 线性遍历每个元素并且比较,找到就返回
  2. 时间复杂度为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接口使用有什么缺点?

  1. 每次获取迭代器时,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接口原理是什么?

  1. 使用Array.Sort接口进行排序,而Array.Sort使用快速排序

List结构总结

  1. List本身效率不是很高,通用性强
  2. List内存分配不合理,创建List实例时需要提前声明元素容量,这样可以减少List扩容次数,进而防止List底层的数组被频繁丢弃,进而减少GC的压力
  3. List本身线程不安全,没有对多线程加锁或同步,并发情况下无法判断_size++的执行顺序,即多线程下使用List应该加安全机制

相关推荐
CSharp精选营1 天前
关系型 vs 非关系型:从原理到选型,一文搞定数据库核心分类
数据结构·nosql·关系型数据库·非关系型数据库·技术选型
刘马想放假5 天前
Modbus 全栈技术解析:TCP、RTU、ASCII、RTU over TCP
数据结构·网络协议
北域码匠6 天前
冒泡排序太慢?鸡尾酒排序双向优化,原生 C# 零第三方库完整代码
数据结构·排序算法·泛型·c# 算法·鸡尾酒排序·原生 c# 开发·冒泡排序优化·嵌入式算法
Darling噜啦啦12 天前
列表转树算法深度解析:从 Map 到 Reduce 的两种实现,面试高频考点
数据结构·算法·面试
小小工匠13 天前
Redis - 事务机制:能实现 ACID 属性吗
数据结构·redis·性能优化·并发·持久化
玖玥拾13 天前
C/C++ 数据结构(七)栈、容器适配器
c语言·数据结构·c++··容器适配器
Qres82114 天前
算法复键——树状数组
数据结构·算法
牛油果子哥q14 天前
并查集(DSU)超精讲,路径压缩、按秩合并、万能模板、连通性判定、最小生成树与刷题实战全解
数据结构·c++·最小生成树·并查集
凌波粒14 天前
LeetCode--491.递增子序列(回溯算法)
数据结构·算法·leetcode
世人万千丶14 天前
成语接龙小应用 - HarmonyOS ArkUI 开发实战-TextInput与List列表-PC版本
华为·list·harmonyos·鸿蒙·鸿蒙系统