C#中的List<T>和Dictionary<TKey, TValue>的底层原理
List<T>和Dictionary<TKey, TValue>本质上上是顺序表,用数组来存储数据,在添加和删除数据时,如果需要调整数组长度,则需要进行数组拷贝。
也可以理解成就是对数组的一种扩展,从而使开发者更方便的调用添加、删除、插入等操作。
所以,优化的思路是,对于大概知道元素的数量时,在实例化时应使用public List(int capacity){}构造函数,来避免在添加元素时,频繁进行数组拷贝,
那么问题来了,既然知道了元素数量,为什么不直接用数组,呜呜呜...
下面是扒的List<T>的部分源码,可见一斑:
List<T>的数据存储在_items里
private T[] _items; private int _size;
List<T>的Add方法:
public void Add(T item) { if (_size == _items.Length) { EnsureCapacity(_size + 1); } _items[_size++] = item; _version++; }
很明显,当数组长度小于数据长度时,则调用EnsureCapacity()方法,我们来看看EnsureCapacity干了个啥
private void EnsureCapacity(int min) { if (_items.Length < min) { int num = (_items.Length == 0) ? 4 : (_items.Length * 2); if ((uint)num > 2146435071u) { num = 2146435071; } if (num < min) { num = min; } Capacity = num; } }
看到这,你可能也有疑问,也没看到有数组拷贝呀,别着急,接着往下看
public int Capacity { [__DynamicallyInvokable] get { return _items.Length; } [__DynamicallyInvokable] set { if (value < _size) { ThrowHelper.ThrowArgumentOutOfRangeException(ExceptionArgument.value, ExceptionResource.ArgumentOutOfRange_SmallCapacity); } if (value == _items.Length) { return; } if (value > 0) { T[] array = new T[value]; if (_size > 0) { Array.Copy(_items, 0, array, 0, _size); } _items = array; } else { _items = _emptyArray; } } }
看到这明白了吧,在Capacity赋值的时候,创建了一个新的数组,然后将旧数组拷贝到新的数组里。
Dictionary<TKey, TValue>的原理也差不多,数据存储在private Entry[] entries;字段里。
Entry是个结构体,存储着Key,Value
private struct Entry { public int hashCode; public int next; public TKey key; public TValue value; }
感兴趣的同学可以自己扒下源码看看,了解了底层逻辑,不但能帮忙你写出更高质量的代码,同时也是一件很有意思的事(跟同事装x的又一神器,哈哈哈),
而且有兴趣的话,你也可以照猫画虎,写出List<T,T>,Dictionary<TKey,TKey, TValue>,在遇到要存储多个数据,或者根据多个数据查找某个值时,可以考虑它,而不用将多个数据封装到一个类里
博客园Jason_c微信打赏码
如果本篇文档对你有帮助,打赏Jason_c根华子吧,他的私房钱被老婆没收了,呜呜!