SortedDictionary<TKey, TValue>和SortedList<TKey, TValue>的功能相同,都用来存储按Key排序的键值对,且无重复。
而内部实现的差异却很大,SortedDictionary<TKey, TValue>的内部实现是红黑二叉搜索树,而SortedList<TKey, TValue>的内部是两个数组,分别存储Key和Value序列。
这就决定了:
- SortedList<TKey, TValue>的内存占用的少,因为树上还要存储左右树和红标识。
- 但是插入的删除的话数组要比树慢。树是O(log2N),数组是O(N)。
- 而插入已排序的数据的话,数组要快。
SortedList<TKey, TValue>的内部数据结构定义如下:
可以看到两个数组用来存储keys和values。而数组的最大长度是2GB.
public class SortedList<TKey, TValue> : IDictionary<TKey, TValue>, ICollection<KeyValuePair<TKey, TValue>>, IEnumerable<KeyValuePair<TKey, TValue>>, IDictionary, ICollection, IEnumerable
{
private static TKey[] emptyKeys = new TKey[0];
private static TValue[] emptyValues = new TValue[0];
private TKey[] keys;
private TValue[] values;
private int _size;
private int version;
private IComparer<TKey> comparer;
private SortedList<TKey, TValue>.KeyList keyList;
private SortedList<TKey, TValue>.ValueList valueList;
[NonSerialized]
private object _syncRoot;
private const int _defaultCapacity = 4;
private const int MaxArrayLength = 2146435071;
而SortedDictionary<TKey, TValue>的内部数据结构定义如下:
内部其实是一个TreeSet的键值对泛型,而TreeSet则是继承于SortedSet红黑树。
public class SortedDictionary<TKey, TValue> : IDictionary<TKey, TValue>, ICollection<KeyValuePair<TKey, TValue>>, IEnumerable<KeyValuePair<TKey, TValue>>, IDictionary, ICollection, IEnumerable
{
[NonSerialized]
private SortedDictionary<TKey, TValue>.KeyCollection keys;
[NonSerialized]
private SortedDictionary<TKey, TValue>.ValueCollection values;
private TreeSet<KeyValuePair<TKey, TValue>> _set;
其中的KeyValuePair<TKey, TValue>的定义如下:
public struct KeyValuePair<TKey, TValue>
{
private TKey key;
private TValue value;
然后对SortedDictionary<TKey, TValue>的增删改查就都是按照SortedSet<T>红黑树的操作了。
而SortedList<TKey, TValue>的增,查,删的实现分别如下:
增:先二分查找到要插入的位置,
public void Add(TKey key, TValue value)
{
if ((object) key == null)
ThrowHelper.ThrowArgumentNullException(ExceptionArgument.key);
int num = Array.BinarySearch<TKey>(this.keys, 0, this._size, key, this.comparer);
if (num >= 0)
ThrowHelper.ThrowArgumentException(ExceptionResource.Argument_AddingDuplicate);
this.Insert(~num, key, value);
}
然后插入:插入的时候要将目标位置之后的所有数据都向后移。。
private void Insert(int index, TKey key, TValue value)
{
if (this._size == this.keys.Length)
this.EnsureCapacity(this._size + 1);
if (index < this._size)
{
Array.Copy((Array) this.keys, index, (Array) this.keys, index + 1, this._size - index);
Array.Copy((Array) this.values, index, (Array) this.values, index + 1, this._size - index);
}
this.keys[index] = key;
this.values[index] = value;
++this._size;
++this.version;
}
删:先通过Key的找到要删除的位置索引
public bool Remove(TKey key)
{
int index = this.IndexOfKey(key);
if (index >= 0)
this.RemoveAt(index);
return index >= 0;
}
找到索引的过程是这样的:还是二分查找。
public int IndexOfKey(TKey key)
{
if ((object) key == null)
ThrowHelper.ThrowArgumentNullException(ExceptionArgument.key);
int num = Array.BinarySearch<TKey>(this.keys, 0, this._size, key, this.comparer);
if (num < 0)
return -1;
else
return num;
}
而根据索引删除的过程如下:把索引之后所有数据前移,然后把最后一个位置赋值为default<T>。
public void RemoveAt(int index)
{
if (index < 0 || index >= this._size)
ThrowHelper.ThrowArgumentOutOfRangeException(ExceptionArgument.index, ExceptionResource.ArgumentOutOfRange_Index);
--this._size;
if (index < this._size)
{
Array.Copy((Array) this.keys, index + 1, (Array) this.keys, index, this._size - index);
Array.Copy((Array) this.values, index + 1, (Array) this.values, index, this._size - index);
}
this.keys[this._size] = default (TKey);
this.values[this._size] = default (TValue);
++this.version;
}
直接根据数组索引Key来get和set Value的实现如下:
public TValue this[TKey key]
{
get
{
int index = this.IndexOfKey(key);
if (index >= 0)
return this.values[index];
ThrowHelper.ThrowKeyNotFoundException();
return default (TValue);
}
set
{
if ((object) key == null)
ThrowHelper.ThrowArgumentNullException(ExceptionArgument.key);
int index = Array.BinarySearch<TKey>(this.keys, 0, this._size, key, this.comparer);
if (index >= 0)
{
this.values[index] = value;
++this.version;
}
else
this.Insert(~index, key, value);
}
}
以上。