DotNet源码学习-HASHSET(初探)
命名空间: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(初探)的更多相关文章
- 从JDK源码学习HashSet和HashTable
HashSet Java中的集合(Collection)有三类,一类是List,一类是Queue,再有一类就是Set. 前两个集合内的元素是有序的,元素可以重复:最后一个集合内的元素无序,但元素不可重 ...
- DotNet 源码学习——QUEUE
1.Queue声明创建对象.(Queue为泛型对象.) public class Queue<T> :IEnumerable<T>,System.Collections.ICo ...
- HashSet源码学习,基于HashMap实现
HashSet源码学习 一).Set集合的主要使用类 1). HashSet 基于对HashMap的封装 2). LinkedHashSet 基于对LinkedHashSet的封装 3). TreeS ...
- spring源码学习之路---IOC初探(二)
作者:zuoxiaolong8810(左潇龙),转载请注明出处,特别说明:本博文来自博主原博客,为保证新博客中博文的完整性,特复制到此留存,如需转载请注明新博客地址即可. 上一章当中我没有提及具体的搭 ...
- Django 源码小剖: 初探 WSGI
Django 源码小剖: 初探 WSGI python 作为一种脚本语言, 已经逐渐大量用于 web 后台开发中, 而基于 python 的 web 应用程序框架也越来越多, Bottle, Djan ...
- Dubbo源码学习--注册中心分析
相关文章: Dubbo源码学习--服务是如何发布的 Dubbo源码学习--服务是如何引用的 注册中心 关于注册中心,Dubbo提供了多个实现方式,有比较成熟的使用zookeeper 和 redis 的 ...
- 【iScroll源码学习01】准备阶段 - 叶小钗
[iScroll源码学习01]准备阶段 - 叶小钗 时间 2013-12-29 18:41:00 博客园-原创精华区 原文 http://www.cnblogs.com/yexiaochai/p/3 ...
- Spring源码学习-容器BeanFactory(一) BeanDefinition的创建-解析资源文件
写在前面 从大四实习至今已一年有余,作为一个程序员,一直没有用心去记录自己工作中遇到的问题,甚是惭愧,打算从今日起开始养成写博客的习惯.作为一名java开发人员,Spring是永远绕不过的话题,它的设 ...
- mybatis源码学习:插件定义+执行流程责任链
目录 一.自定义插件流程 二.测试插件 三.源码分析 1.inteceptor在Configuration中的注册 2.基于责任链的设计模式 3.基于动态代理的plugin 4.拦截方法的interc ...
随机推荐
- Java入门 - 语言基础 - 22.异常处理
原文地址:http://www.work100.net/training/java-exception.html 更多教程:光束云 - 免费课程 异常处理 序号 文内章节 视频 1 概述 2 Exce ...
- 约束路由 用正则表达式约束路由 Constraining a Route Using a Regular Expression 精通ASP-NET-MVC-5-弗瑞曼
- CentOS7下部署2套Python版本共存
参考地址:https://www.cnblogs.com/xuaijun/p/7985245.html 源码的安装一般由3个步骤组成:配置(configure).编译(make).安装(make in ...
- iperf安装使用教程
https://linoxide.com/monitoring-2/install-iperf-test-network-speed-bandwidth/
- Java虚拟机系列一:一文搞懂 JVM 架构和运行时数据区
前言 之前写博客一直比较随性,主题也很随意,就是想到什么写什么,对什么感兴趣就写什么.虽然写起来无拘无束,自在随意,但也带来了一些问题,每次写完一篇后就要去纠结下一篇到底写什么,看来选择太多也不是好事 ...
- CCNA的基础知识及要点
一.CCNA中的基础知识及要点: 2.网线的制作:568B:橙白,橙,绿白,蓝,蓝白,绿,棕白,棕 568A的排线顺序从左到右依次为:白绿.绿.白橙.蓝.白蓝.橙.白棕.棕.实验目的:初学者常为做网线 ...
- tmobst4
(单选题)HTML代码: <table> <tr><td>Value 1</td><td></td></tr> &l ...
- Java中正确终止线程的方法
Thread类中有一个已经废弃的 stop() 方法,它可以终止线程,但由于它不管三七二十一,直接终止线程,所以被废弃了.比如,当线程被停止后还需要进行一些善后操作(如,关闭外部资源),使用这个方法就 ...
- BZOJ 1087 [SCOI2005]互不侵犯King(状压DP)
题意:在N×N的棋盘里面放K个国王,使他们互不攻击,共有多少种摆放方案.国王能攻击到它上下左右,以及左上左下右上右下八个方向上附近的各一个格子,共8个格子.n<=9 思路:状压dp,dp[i][ ...
- python库之numpy学习---nonzero()用法
当使用布尔数组直接作为下标对象或者元组下标对象中有布尔数组时,都相当于用nonzero()将布尔数组转换成一组整数数组,然后使用整数数组进行下标运算. nonzeros(a)返回数组a中值不为零的元素 ...