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 ...
随机推荐
- 「 神器 」在线PDF文件管理工具和图片编辑神器
每天进步一丢丢,连接梦与想 在线PDF文件管理工具 完全免费的PDF文件在线管理工具,其功能包括:合并PDF文件.拆分PDF文件.压缩PDF文件.Office文件转换为PDF文件.PDF文件转换为JP ...
- 【java面试】Web篇
1.AJAX创建步骤 step1. 创建XMLHttpRequest对象,也就是创建一个异步调用对象: step2. 创建一个新的HTTP请求,并指定改HTTP请求的方法.URL以及验证信息: s ...
- umake ide -h
umake ide -husage: umake ide [-h] {netbeans,idea,clion,eclipse,atom,idea-ultimate,ec ...
- mva 的 第一弹 ASP.NET SignalR
弹弹弹 弹走 占位 补齐
- Java 单向队列及环形队列
队列的特点 1.可以使用数组和链表两种方式来实现. 2.遵循先入先出(FIFO)的规则,即先进入的数据先出. 3.属于有序列表. 图解实现过程: 1.定义一个固定长度的数组,长度为maxSize. ...
- [ Python入门教程 ] Python中日期时间datetime模块使用实例
Python中datetime模块提供强大易用的日期处理功能,用于记录程序操作或修改时间.时间计算.日志时间显示等功能.datatime模块重新封装了time模块,提供的类包括date.time.da ...
- Mac系统 python2.7中安装MySQLdb
由于要在python2.7上使用到MySQLdb连接数据库,所以要安装MySQLdb,也就是MySQL-Python.安装之前已经有人告诉我,这个东西比较难装,果然我也遇到好多问题,在百度找了半天,发 ...
- 内网IP的解释
https://baike.baidu.com/item/%E5%86%85%E7%BD%91ip/8881186?fr=aladdin
- SMB信息泄露
开门见山 1. 用netdiscover -r 扫描与攻击机同一网段的靶机,发现PCS 2. 扫描靶场开放信息 3. 挖掘靶场全部信息 4. 针对SMB协议,使用空口令,若口令尝试登录,并查看敏感文件 ...
- HanLP《自然语言处理入门》笔记--1.新手上路
1. 新手上路 自然语言处理(Natural Language Processing,NLP)是一门融合了计算机科学.人工智能及语言学的交叉学科,它们的关系如下图所示.这门学科研究的是如何通过机器学习 ...