C# HashSet<T>源码浅析

所在框架版本:.Net Framework 4.0.30319

1.部分变量

private const int Lower31BitMask = 2147483647;
private const int StackAllocThreshold = 100;
private const int ShrinkThreshold = 3;
private const string CapacityName = "Capacity";
private const string ElementsName = "Elements";
private const string ComparerName = "Comparer";
private const string VersionName = "Version";
private int[] m_buckets;  //桶数组
private HashSet<T>.Slot[] m_slots;   //元素数组
private int m_count;
private int m_lastIndex;   
private int m_freeList;   //空闲元素数组下标
private IEqualityComparer<T> m_comparer;
private int m_version;
private SerializationInfo m_siInfo;

2.构造函数

public HashSet(int capacity, IEqualityComparer<T> comparer)  //参数:容量,比较器
      : this(comparer)
    {
      if (capacity < 0)
        throw new ArgumentOutOfRangeException(nameof (capacity));
      if (capacity <= 0)
        return;
      this.Initialize(capacity);
    }

3.初始化

private void Initialize(int capacity)
    {
      int prime = HashHelpers.GetPrime(capacity);   //获得大于容量的最小质数
      this.m_buckets = new int[prime];   //初始化桶数组
      this.m_slots = new HashSet<T>.Slot[prime];   //初始化元素数组
    }

4.添加

public bool Add(T item) => this.AddIfNotPresent(item);
private bool AddIfNotPresent(T value)
    {
      if (this.m_buckets == null)   //如果桶数组为空
        this.Initialize(0);   //重新初始化
      int hashCode = this.InternalGetHashCode(value);   //通过值获取hash值
      int index1 = hashCode % this.m_buckets.Length;   //获得桶下标
      int num = 0;   //hash碰撞次数
      for (int index2 = this.m_buckets[hashCode % this.m_buckets.Length] - 1; index2 >= 0; index2 = this.m_slots[index2].next)   //遍历元素链
      {
        if (this.m_slots[index2].hashCode == hashCode && this.m_comparer.Equals(this.m_slots[index2].value, value))   //如果某个元素已经存在
          return false;
        ++num;
      }
      int index3;
      if (this.m_freeList >= 0)   //如果存在空闲位置
      {
        index3 = this.m_freeList;  
        this.m_freeList = this.m_slots[index3].next;  
      }
      else
      {
        if (this.m_lastIndex == this.m_slots.Length)   //满了
        {
          this.IncreaseCapacity();   //扩容
          index1 = hashCode % this.m_buckets.Length;   //重新确定桶下标
        }
        index3 = this.m_lastIndex;   
        ++this.m_lastIndex;
      }
      //元素数组赋值
      this.m_slots[index3].hashCode = hashCode;   
      this.m_slots[index3].value = value;
      this.m_slots[index3].next = this.m_buckets[index1] - 1;  
      this.m_buckets[index1] = index3 + 1;   
      ++this.m_count;
      ++this.m_version;  //版本号更新
      if (num > 100 && HashHelpers.IsWellKnownEqualityComparer((object) this.m_comparer))   // 如果碰撞次数大于设置的最大碰撞次数,那么触发Hash碰撞扩容
      {
        this.m_comparer = (IEqualityComparer<T>) HashHelpers.GetRandomizedEqualityComparer((object) this.m_comparer);
        this.SetCapacity(this.m_buckets.Length, true);
      }
      return true;
    }

5.查找

public bool Contains(T item)
    {
      if (this.m_buckets != null)    
      {
        int hashCode = this.InternalGetHashCode(item);  //获得值对应的hash值
        for (int index = this.m_buckets[hashCode % this.m_buckets.Length] - 1; index >= 0; index = this.m_slots[index].next)   //遍历元素链
        {
          if (this.m_slots[index].hashCode == hashCode && this.m_comparer.Equals(this.m_slots[index].value, item))
            return true;
        }
      }
      return false;
    }

6.移除

public bool Remove(T item)
    {
      if (this.m_buckets != null)
      {
        int hashCode = this.InternalGetHashCode(item);   //获得hash值
        int index1 = hashCode % this.m_buckets.Length;   //获得桶数组下标
        int index2 = -1;    // 用于确定是否当前entries链的第一个元素
        for (int index3 = this.m_buckets[index1] - 1; index3 >= 0; index3 = this.m_slots[index3].next)    //遍历元素链
        {
          if (this.m_slots[index3].hashCode == hashCode && this.m_comparer.Equals(this.m_slots[index3].value, item))
          {
            if (index2 < 0)      //如果要移除的是第一个元素,则对应桶存储当前元素的Next元素下标
              this.m_buckets[index1] = this.m_slots[index3].next + 1;
            else   //不是第一个,需要将上一个元素的Next指向当前元素的Next,相当于越过了当前元素
              this.m_slots[index2].next = this.m_slots[index3].next;
             //元素数组数据重置
            this.m_slots[index3].hashCode = -1;   
            this.m_slots[index3].value = default (T);
            this.m_slots[index3].next = this.m_freeList;
            --this.m_count;
            ++this.m_version;   //版本号更新
            if (this.m_count == 0)
            {
              this.m_lastIndex = 0;
              this.m_freeList = -1;  
            }
            else
              this.m_freeList = index3;  //freeList等于当前的元素数组的位置,下一次添加元素会优先添加到该位置
            return true;
          }
          index2 = index3;   //不断在这个链表上往后移动
        }
      }
      return false;
    }
  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值