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 ...
随机推荐
- [bzoj3926] [loj2137] [Zjoi2015] 诸神眷顾的幻想乡
Description 幽香是全幻想乡里最受人欢迎的萌妹子,这天,是幽香的2600岁生日,无数幽香的粉丝到了幽香家门前的太阳花田上来为幽香庆祝生日. 粉丝们非常热情,自发组织表演了一系列节目给幽香看. ...
- 隐隐约约 听 RazorEngine 在 那里 据说 生成代码 很 美。
这只是 一个开始 ....
- ip 地址库 这个 准么 呢
- NOI2.5 1490:A Knight's Journey
描述 Background The knight is getting bored of seeing the same black and white squares again and again ...
- Educational Codeforces Round 81 (Rated for Div. 2) B. Infinite Prefixes
题目链接:http://codeforces.com/contest/1295/problem/B 题目:给定由0,1组成的字符串s,长度为n,定义t = sssssss.....一个无限长的字符串. ...
- 使用IDEA构建Spring-boot多模块项目配置流程
使用IDEA构建Spring-boot多模块项目配置流程 1.创建项目 点击Create New Project 在左侧选中Spring Initializer,保持默认配置,点击下一步. 在Grou ...
- 1240: 函数strcmp的设计
#include <string.h>#include <stdio.h>int mycmp(char*s1,char*s2);int main(){ int sum; cha ...
- spring5.0源码项目搭建
一.准备相应环境以及下载spring项目 Ps:此处只讲解安装gradle 1.JDK安装 2.Idea安装 3.gradle安装 Gradle下载路径:https://services.gradle ...
- 15、WAN
WAN wide area network 覆盖较大地理范围的数据通信网络使用网络提供商和电信公司所提供的传输设施传输数据 通过不同WAN协议,将LAN延伸到远程站点的其他LAN广域网接入处于OSI七 ...
- VS2013下搭建SDL开发环境
什么是SDL? SDL是 "Simple DirectMedia Layer"的缩写,它是一个开源的项目. 为多媒体编程而设计 SDL是一个跨平台的多媒体库,它通过OpenGL和2 ...