命名空间:System.Collections.Generic

先看一下官方说明:类提供了高级的设置操作。集是不包含重复元素的集合,其元素无特定顺序。

HashSet <T>对象的容量是对象可以容纳的元素数。当向对象添加元素时,HashSet <T>对象的容量会自动增加。

HashSet<String> hashSet = new HashSet<string>();
hashSet.Add("test");
hashSet.Add("test");
Console.WriteLine(hashSet.Count);

打印结果:1

HashSet的默认构造方法:

public HashSet()
: this((IEqualityComparer<T>?)null)
{ }

注:: this语法为调用自身对象的其他构造方法。

public HashSet(IEqualityComparer<T>? comparer)
{
if (comparer == EqualityComparer<T>.Default)
{
comparer = null;
}
_comparer = comparer;
_lastIndex = ;
_count = ;
_freeList = -;
_version = ;
}

第二中创建方式,将集合作为参数。

List<string> list = new List<string>();
list.Add("test");
list.Add("test");
HashSet<string> hSet = new HashSet<string>(list);
Console.WriteLine(hSet.Count);

此时控台输出:1

此时调用的构造方法为:

public HashSet(IEnumerable<T> collection, IEqualityComparer<T>? comparer)
: this(comparer)
{
if (collection == null)
{
throw new ArgumentNullException(nameof(collection));
}
var otherAsHashSet = collection as HashSet<T>;
if (otherAsHashSet != null && AreEqualityComparersEqual(this, otherAsHashSet))
{
CopyFrom(otherAsHashSet);
}
else
{
// to avoid excess resizes, first set size based on collection's count. Collection
// may contain duplicates, so call TrimExcess if resulting hashset is larger than
// threshold
ICollection<T>? coll = collection as ICollection<T>;
int suggestedCapacity = coll == null ? : coll.Count;
Initialize(suggestedCapacity);
UnionWith(collection);
if (_count > && _slots.Length / _count > ShrinkThreshold)
{
TrimExcess();
}
}
}

在该构造方法中若存在重复值则通过查找大于或等于容量的下一个质数来使用建议的容量。

private int Initialize(int capacity)
{
Debug.Assert(_buckets == null, "Initialize was called but _buckets was non-null");
int size = HashHelpers.GetPrime(capacity);
_buckets = new int[size];
_slots = new Slot[size];
return size;
}

下面为生成质数方法:

public static int GetPrime(int min)
{
if (min < )
throw new ArgumentException(SR.Arg_HTCapacityOverflow);
foreach (int prime in s_primes)
{
if (prime >= min)
return prime;
}
// Outside of our predefined table. Compute the hard way.
for (int i = (min | ); i < int.MaxValue; i += )
{
if (IsPrime(i) && ((i - ) % HashPrime != ))
return i;
}
return min;
}

再次扩展-》

public static bool IsPrime(int candidate)
{
if ((candidate & ) != )
{
int limit = (int)Math.Sqrt(candidate);//取平方
for (int divisor = ; divisor <= limit; divisor += )
{
if ((candidate % divisor) == )
return false;
}
return true;
}
return candidate == ;
}
internal struct Slot
{
internal int hashCode; // Lower 31 bits of hash code, -1 if unused
internal int next; // Index of next entry, -1 if last
internal T value;
}

存储LIst集合:

public void UnionWith(IEnumerable<T> other)
{
if (other == null)
{
throw new ArgumentNullException(nameof(other));
}
foreach (T item in other)
{
AddIfNotPresent(item);
}
}

继续往下追踪:

private bool AddIfNotPresent(T value)
{
if (_buckets == null)
{
Initialize();
} int hashCode;
int bucket;
int collisionCount = ;
Slot[] slots = _slots; IEqualityComparer<T>? comparer = _comparer; if (comparer == null)
{
//取HASHCODE
hashCode = value == null ? : InternalGetHashCode(value.GetHashCode());
bucket = hashCode % _buckets!.Length; if (default(T)! != null) // TODO-NULLABLE: default(T) == null warning (https://github.com/dotnet/roslyn/issues/34757)
{
for (int i = _buckets[bucket] - ; i >= ; i = slots[i].next)
{
if (slots[i].hashCode == hashCode && EqualityComparer<T>.Default.Equals(slots[i].value, value))
{
return false;
} if (collisionCount >= slots.Length)
{
// The chain of entries forms a loop, which means a concurrent update has happened.
throw new InvalidOperationException(SR.InvalidOperation_ConcurrentOperationsNotSupported);
}
collisionCount++;
}
}
else
{
// Object type: Shared Generic, EqualityComparer<TValue>.Default won't devirtualize
// https://github.com/dotnet/coreclr/issues/17273
// So cache in a local rather than get EqualityComparer per loop iteration
EqualityComparer<T> defaultComparer = EqualityComparer<T>.Default; for (int i = _buckets[bucket] - ; i >= ; i = slots[i].next)
{
if (slots[i].hashCode == hashCode && defaultComparer.Equals(slots[i].value, value))
{
return false;
} if (collisionCount >= slots.Length)
{
// The chain of entries forms a loop, which means a concurrent update has happened.
throw new InvalidOperationException(SR.InvalidOperation_ConcurrentOperationsNotSupported);
}
collisionCount++;
}
}
}
else
{
hashCode = value == null ? : InternalGetHashCode(comparer.GetHashCode(value));
bucket = hashCode % _buckets!.Length; for (int i = _buckets[bucket] - ; i >= ; i = slots[i].next)
{
if (slots[i].hashCode == hashCode && comparer.Equals(slots[i].value, value))
{
return false;
} if (collisionCount >= slots.Length)
{
// The chain of entries forms a loop, which means a concurrent update has happened.
throw new InvalidOperationException(SR.InvalidOperation_ConcurrentOperationsNotSupported);
}
collisionCount++;
}
} int index;
if (_freeList >= )
{
index = _freeList;
_freeList = slots[index].next;
}
else
{
if (_lastIndex == slots.Length)
{
IncreaseCapacity();
// this will change during resize
slots = _slots;
bucket = hashCode % _buckets.Length;
}
index = _lastIndex;
_lastIndex++;
}
slots[index].hashCode = hashCode;
slots[index].value = value;
slots[index].next = _buckets[bucket] - ;
_buckets[bucket] = index + ;
_count++;
_version++; return true;
}
private const int Lower31BitMask = 0x7FFFFFFF;

获取内部的HASHCODE

private static int InternalGetHashCode(T item, IEqualityComparer<T>? comparer)
{
if (item == null)
{
return ;
}
int hashCode = comparer?.GetHashCode(item) ?? item.GetHashCode();
return hashCode & Lower31BitMask;
}

划重点-》

slots[index].hashCode = hashCode;
slots[index].value = value;
slots[index].next = _buckets[bucket] - ;

最终列表中的值存储到结构中。

使用对象初始化HASHSET时,如果相同

HashSet<string> hashSet = new HashSet<string>();
hashSet.Add("test");
hashSet.Add("test");
HashSet<string> hSet = new HashSet<string>(hashSet);
private void CopyFrom(HashSet<T> source)
{
int count = source._count;
if (count == )
{
// As well as short-circuiting on the rest of the work done,
// this avoids errors from trying to access otherAsHashSet._buckets
// or otherAsHashSet._slots when they aren't initialized.
return;
} int capacity = source._buckets!.Length;
int threshold = HashHelpers.ExpandPrime(count + ); if (threshold >= capacity)
{
_buckets = (int[])source._buckets.Clone();
_slots = (Slot[])source._slots.Clone(); _lastIndex = source._lastIndex;
_freeList = source._freeList;
}
else
{
int lastIndex = source._lastIndex;
Slot[] slots = source._slots;
Initialize(count);
int index = ;
for (int i = ; i < lastIndex; ++i)
{
int hashCode = slots[i].hashCode;
if (hashCode >= )
{
AddValue(index, hashCode, slots[i].value);
++index;
}
}
Debug.Assert(index == count);
_lastIndex = index;
}
_count = count;
}
public static int ExpandPrime(int oldSize)//返回要增长到的哈希表大小
{
int newSize = * oldSize; // Allow the hashtables to grow to maximum possible size (~2G elements) before encountering capacity overflow.
// Note that this check works even when _items.Length overflowed thanks to the (uint) cast
if ((uint)newSize > MaxPrimeArrayLength && MaxPrimeArrayLength > oldSize)
{
Debug.Assert(MaxPrimeArrayLength == GetPrime(MaxPrimeArrayLength), "Invalid MaxPrimeArrayLength");
return MaxPrimeArrayLength;
} return GetPrime(newSize);
}

这里只贴出HashSet声明创建对象的两种方式。

下篇再研究具体实现〜

DotNet源码学习-HASHSET(初探)的更多相关文章

  1. 从JDK源码学习HashSet和HashTable

    HashSet Java中的集合(Collection)有三类,一类是List,一类是Queue,再有一类就是Set. 前两个集合内的元素是有序的,元素可以重复:最后一个集合内的元素无序,但元素不可重 ...

  2. DotNet 源码学习——QUEUE

    1.Queue声明创建对象.(Queue为泛型对象.) public class Queue<T> :IEnumerable<T>,System.Collections.ICo ...

  3. HashSet源码学习,基于HashMap实现

    HashSet源码学习 一).Set集合的主要使用类 1). HashSet 基于对HashMap的封装 2). LinkedHashSet 基于对LinkedHashSet的封装 3). TreeS ...

  4. spring源码学习之路---IOC初探(二)

    作者:zuoxiaolong8810(左潇龙),转载请注明出处,特别说明:本博文来自博主原博客,为保证新博客中博文的完整性,特复制到此留存,如需转载请注明新博客地址即可. 上一章当中我没有提及具体的搭 ...

  5. Django 源码小剖: 初探 WSGI

    Django 源码小剖: 初探 WSGI python 作为一种脚本语言, 已经逐渐大量用于 web 后台开发中, 而基于 python 的 web 应用程序框架也越来越多, Bottle, Djan ...

  6. Dubbo源码学习--注册中心分析

    相关文章: Dubbo源码学习--服务是如何发布的 Dubbo源码学习--服务是如何引用的 注册中心 关于注册中心,Dubbo提供了多个实现方式,有比较成熟的使用zookeeper 和 redis 的 ...

  7. 【iScroll源码学习01】准备阶段 - 叶小钗

    [iScroll源码学习01]准备阶段 - 叶小钗 时间 2013-12-29 18:41:00 博客园-原创精华区 原文  http://www.cnblogs.com/yexiaochai/p/3 ...

  8. Spring源码学习-容器BeanFactory(一) BeanDefinition的创建-解析资源文件

    写在前面 从大四实习至今已一年有余,作为一个程序员,一直没有用心去记录自己工作中遇到的问题,甚是惭愧,打算从今日起开始养成写博客的习惯.作为一名java开发人员,Spring是永远绕不过的话题,它的设 ...

  9. mybatis源码学习:插件定义+执行流程责任链

    目录 一.自定义插件流程 二.测试插件 三.源码分析 1.inteceptor在Configuration中的注册 2.基于责任链的设计模式 3.基于动态代理的plugin 4.拦截方法的interc ...

随机推荐

  1. .net Core发布至IIS完全手册带各种踩坑

    服务器环境配置 和各位大爷报告一下我的服务器环境 : Windows Server 2012 iis 8 小插曲开始: 运维大哥在昨天给了我一台新的server 0环境开始搭建 . 并且没有安装任何的 ...

  2. 剑指offer--二维数组中查找

    剑指offer--二维数组中查找 题目描述 在一个二维数组中(每个一维数组的长度相同),每一行都按照从左到右递增的顺序排序, 每一列都按照从上到下递增的顺序排序.请完成一个函数,输入这样的一个二维数组 ...

  3. GitHub Pages 与 Gitee Pages 上的 Jekyll

    GitHub 与 Gitee 提供的 Pages 服务中,均内嵌了 Jekyll 支持(Gitee 还提供了 Hugo 与 Hexo 支持).所谓「支持」,即指这些生成工具挂在云端:你只需要提供原始代 ...

  4. 什么是 Trait

    Trait 是从 PHP 5.4 加入的一种细粒度代码复用的语法.以下是官方手册对 Trait 的描述: Trait 是为类似 PHP 的单继承语言而准备的一种代码复用机制.Trait 为了减少单继承 ...

  5. DWZ框架-- Dialog点击保存后不能自动关闭

    案例 今天在用DWZ框架做添加功能时,发现在对话框保存成功后,后端返回正确的json格式,但对话框不能自动关闭窗口,而且保存后自动跳回主页,没有停留在当前用户列表页面,具体错误过程重现如下: 1.打开 ...

  6. mint ui的tabBar监听路由变化实现tabBar切换

    说明 最近学习vue,使用了mint ui的tabBar,感觉好难受,结合 tab-container使用更难受,因为它不是根据路由来切换页面的.mui与它基本相反,因此它能根据搜索栏的路由变化,相应 ...

  7. c++中的动态内存分配

    使用new和delete动态的分配和释放内存 使用new来分配新的内存块,通常情况下,如果成功,new将返回一个指针,指向分配的内存,否则将引发异常,使用new时,需要指定要为那种数据类型分配内存: ...

  8. java数据域初始化

    1.在声明中赋值 /** * Created by N3verL4nd on 2016/11/19. */ class Test{ private String str = "Hello W ...

  9. Sqli-Labs 闯关 less 42-49

    Less 42 这一关一进去看着像前面的二次注入.发现也注入不了.. 我们观察代码发现这一关用的是堆叠注入. 登陆的这里可以看到login_password登陆的时候并没有使用mysqli_real_ ...

  10. HDU_2510_打表

    http://acm.hdu.edu.cn/showproblem.php?pid=2510 dfs打表. #include<iostream> #include<cstdio> ...