读书笔记——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应该加安全机制

相关推荐
想要成为糕糕手7 小时前
前端必修课:JavaScript 数组与数据结构底层逻辑全解析
javascript·数据结构·面试
tyung9 小时前
Go 手写 Wait-Free SPSC 无界队列:无 CAS、无锁、泛型节点池
数据结构·后端·go
Chen_harmony9 小时前
一、数据结构概念和复杂度计算
数据结构
小欣加油10 小时前
leetcode287寻找重复数
数据结构·c++·算法·leetcode
fie888912 小时前
LBP + HOG 特征检测与识别 MATLAB 实现
数据结构·算法·matlab
退休倒计时13 小时前
【每日一题】LeetCode 15. 三数之和 TypeScript
数据结构·算法·leetcode·typescript
AbandonForce13 小时前
滑动窗口:定长滑动窗口与不定长滑动窗口
数据结构·c++·算法
炸薯条!14 小时前
二叉树的链式表示(2)
java·数据结构·算法
YHHLAI14 小时前
JavaScript 数据结构精讲:数组底层与实战避坑
开发语言·javascript·数据结构
Coder-magician14 小时前
《代码随想录》刷题打卡day12:二叉树part02
数据结构·c++·算法