.NET源码中的SortedDictionary<TKey, TValue>和SortedList<TKey, TValue>

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);
      }
    }

以上。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值