1、HashSet底层其实是一个HashMap容器,HashSet的无参构造方法是创建一个HashMap对象

 private transient HashMap<E, Object> map;public HashSet() {this.map = new HashMap();}

2、add方法添加数据实则是调用其成员变量map的put方法,PRESENT 就是一个Object类,目前还不太理解作用,把其作用当为占位


private static final Object PRESENT = new Object();public boolean add(E var1) {return this.map.put(var1, PRESENT) == null;}

3、put方法调用putVal方法

public V put(K var1, V var2) {return this.putVal(hash(var1), var1, var2, false, true);}

4、hash方法通过要存储的数据的哈希值经过一些运算返回一个int型整数

static final int hash(Object var0) {int var1;return var0 == null ? 0 : (var1 = var0.hashCode()) ^ var1 >>> 16;}

transient HashMap.Node<K, V>[] table;final V putVal(int var1, K var2, V var3, boolean var4, boolean var5) {HashMap.Node[] var6;int var8;if ((var6 = this.table) == null || (var8 = var6.length) == 0) {var8 = (var6 = this.resize()).length;}Object var7;int var9;if ((var7 = var6[var9 = var8 - 1 & var1]) == null) {var6[var9] = this.newNode(var1, var2, var3, (HashMap.Node)null);} else {Object var10;Object var11;if (((HashMap.Node)var7).hash == var1 && ((var11 = ((HashMap.Node)var7).key) == var2 || var2 != null && var2.equals(var11))) {var10 = var7;} else if (var7 instanceof HashMap.TreeNode) {var10 = ((HashMap.TreeNode)var7).putTreeVal(this, var6, var1, var2, var3);} else {int var12 = 0;while(true) {if ((var10 = ((HashMap.Node)var7).next) == null) {((HashMap.Node)var7).next = this.newNode(var1, var2, var3, (HashMap.Node)null);if (var12 >= 7) {this.treeifyBin(var6, var1);}break;}if (((HashMap.Node)var10).hash == var1 && ((var11 = ((HashMap.Node)var10).key) == var2 || var2 != null && var2.equals(var11))) {break;}var7 = var10;++var12;}}if (var10 != null) {Object var13 = ((HashMap.Node)var10).value;if (!var4 || var13 == null) {((HashMap.Node)var10).value = var3;}this.afterNodeAccess((HashMap.Node)var10);return var13;}}++this.modCount;if (++this.size > this.threshold) {this.resize();}this.afterNodeInsertion(var5);return null;}

5、putVal方法首先声明一个节点型数组var6(Node是HashMap的内部类,Empty的实现类),int型整数var8,table为HashMap的成员变量,一个跟var6相同类型的节点型数组,将table的地址赋给了var6并且进行判断是否为空或长度是否为0,如果是的话需要调用resize()方法来创建table。Object类var7其实是一个节点,Node类的对象。因为存储的时候是通过数据的hash值和table的长度运算得出存储位置,再通过判断是否为null(默认都是null),如果为空的话直接将数据打包成一个节点替换掉原来的null

if ((var7 = var6[var9 = var8 - 1 & var1]) == null) {var6[var9] = this.newNode(var1, var2, var3, (HashMap.Node)null);}

(去重)如果不为空的话,判断存储位置var7的哈希值是否与数据传递过来的哈希值相等,然后再判断它的key地址是否相等,最后再用equals()方法判断,相同的话记住这个节点(其实这个地方不太理解)

if (((HashMap.Node)var7).hash == var1 && ((var11 = ((HashMap.Node)var7).key) == var2 || var2 != null && var2.equals(var11))) {var10 = var7;}

不相同的话再判断存储位置的这个节点是否是树形结构(红黑树结构),如果是的话调用putTreeVal()方法将其存储到红黑树

} else if (var7 instanceof HashMap.TreeNode) {var10 = ((HashMap.TreeNode)var7).putTreeVal(this, var6, var1, var2, var3);}

因为jdk1.8之前的版本table中的节点结构是链表结构,但是由于一个节点上存储过多数据可能导致查询变慢,所以当节点中的链表长度为8时调用HashMap中的treeifyBin()方法将链表结构转为红黑树结构,提高查找效率var6为HashMap中要转变节点结构的节点数组,即table,var1是传递过来的存储位置哈希值,通过这两个参数找到要转变结构的节点(这个方法没有去深究了,不知道对不对,有错误请指出)

通过while循环节点中的链表中的数据,链表的最后一个也是有连接的,只不过是一个null,所以判断为null的话就直接将要存储的数据打包一个节点连接上去,在判断为null之前,节点中的数据都会遍历一遍,即判断一下是否与其中的数据相同,如果相同就会break退出循环

else {int var12 = 0;while(true) {if ((var10 = ((HashMap.Node)var7).next) == null) {((HashMap.Node)var7).next = this.newNode(var1, var2, var3, (HashMap.Node)null);if (var12 >= 7) {this.treeifyBin(var6, var1);}break;}if (((HashMap.Node)var10).hash == var1 && ((var11 = ((HashMap.Node)var10).key) == var2 || var2 != null && var2.equals(var11))) {break;}var7 = var10;++var12;}}

6、这个代码是用来替换Valued的,当var10不为空的情况只有上文中的去重时break才会发生,var4为固定的FALSE,((HashMap.Node)var10).value = var3;这样就替换了原来的Value

if (var10 != null) {Object var13 = ((HashMap.Node)var10).value;if (!var4 || var13 == null) {((HashMap.Node)var10).value = var3;}this.afterNodeAccess((HashMap.Node)var10);return var13;}

7、resize方法

第一次调用会创建一个节点数组table,之后再调用的话就是为了扩容,table含有一个加载因子为0.75,当存储的节点长度为table长度*加载因子时就会扩容,长度变为当前的两倍

但是之前有一个困扰我很久的误区,因为存储位置是通过数据的哈希值和table的长度来计算的,如果长度变了是否就会影响存储位置从而产生相同对象存储进去。
后来才知道扩容后会重新计算table中节点的位置,会调用rehash()方法重新计算地址

final HashMap.Node<K, V>[] resize() {HashMap.Node[] var1 = this.table;int var2 = var1 == null ? 0 : var1.length;int var3 = this.threshold;int var5 = 0;int var4;if (var2 > 0) {if (var2 >= 1073741824) {this.threshold = 2147483647;return var1;}if ((var4 = var2 << 1) < 1073741824 && var2 >= 16) {var5 = var3 << 1;}} else if (var3 > 0) {var4 = var3;} else {var4 = 16;var5 = 12;}if (var5 == 0) {float var6 = (float)var4 * this.loadFactor;var5 = var4 < 1073741824 && var6 < 1.07374182E9F ? (int)var6 : 2147483647;}this.threshold = var5;HashMap.Node[] var14 = (HashMap.Node[])(new HashMap.Node[var4]);this.table = var14;if (var1 != null) {for(int var7 = 0; var7 < var2; ++var7) {HashMap.Node var8;if ((var8 = var1[var7]) != null) {var1[var7] = null;if (var8.next == null) {var14[var8.hash & var4 - 1] = var8;} else if (var8 instanceof HashMap.TreeNode) {((HashMap.TreeNode)var8).split(this, var14, var7, var2);} else {HashMap.Node var9 = null;HashMap.Node var10 = null;HashMap.Node var11 = null;HashMap.Node var12 = null;HashMap.Node var13;do {var13 = var8.next;if ((var8.hash & var2) == 0) {if (var10 == null) {var9 = var8;} else {var10.next = var8;}var10 = var8;} else {if (var12 == null) {var11 = var8;} else {var12.next = var8;}var12 = var8;}var8 = var13;} while(var13 != null);if (var10 != null) {var10.next = null;var14[var7] = var9;}if (var12 != null) {var12.next = null;var14[var7 + var2] = var11;}}}}}return var14;}

初学者自己的理解,有不对的请指出

HashSet底层原理简单详解相关推荐

  1. java web底层原理_详解Java开发Web应用程序的底层原理

    前言 前面一篇文章,我从整个应用程序的整体以及跟运行环境的关系简单聊了一下我们现在常用的Spring框架的设计基础和准则,其中主要是控制反转和依赖注入,以及容器化编程等概念. 这里我不想去复述这些概念 ...

  2. 抖音推荐算法原理全文详解

    阅读目录 一.系统概览 二.内容分析 三.用户标签 四.评估分析 五.内容安全 抖音推荐算法原理全文详解 本次分享将主要介绍今日头条推荐系统概览以及内容分析.用户标签.评估分析,内容安全等原理. 回到 ...

  3. 趣谈网络协议-第二模块-底层网络知识详解:4陌生的数据中心2CDN和数据中心

    趣谈网络协议-第二模块-底层网络知识详解:4陌生的数据中心2CDN和数据中心 1:CDN:你去小卖部取过快递么? 使用"中间仓库"来优化 网络中的"就近配送" ...

  4. 趣谈网络协议-第二模块-底层网络知识详解:2最重要的传输层

    趣谈网络协议-第二模块-底层网络知识详解:2最重要的传输层 1:第10讲 | UDP协议:因性善而简单,难免碰到"城会玩" TCP 和 UDP 有哪些区别? UDP 包头是什么样的 ...

  5. [crypto]-01-对称加解密AES原理概念详解

    1.对称加解密 术语:P是明文,C是密文,K是密钥,E是加密算法,D是解密算 (1).常用的对称加解密有哪些? (2).加解密的模式 [ecb]这种模式是将整个明文分成若干段相同的小段,然后对每一小段 ...

  6. Linux 下 TC 命令原理及详解<一>

    文章目录 1 前言 2 相关概念 3 使用TC 4 创建HTB队列 5 为根队列创建相应的类别 6 为各个类别设置过滤器 7 复杂的实例 Linux 下 TC 命令原理及详解<一> Lin ...

  7. 今日头条推荐算法原理全文详解之一

    本次分享将主要介绍今日头条推荐系统概览以及内容分析.用户标签.评估分析,内容安全等原理. 今日头条推荐算法原理全文详解 今日头条 数据分析 产品经理 产品 好文分享 第1张 一.系统概览 推荐系统,如 ...

  8. 白加黑加载方式_“白加黑减”即曝光补偿的应用方法及原理全面详解——致新手新新手...

    "白加黑减"即曝光补偿的应用方法及原理全面详解 --致新手新新手 本文对曝光补偿这一摄影基本技术及原理做一全面详细的分析和解释,旨在让受此困惑的新手能够从原理上彻底理解" ...

  9. 今日头条推荐算法原理全文详解之四

    三.用户标签 内容分析和用户标签是推荐系统的两大基石.内容分析涉及到机器学习的内容多一些,相比而言,用户标签工程挑战更大. 今日头条推荐算法原理全文详解 今日头条 数据分析 产品经理 产品 好文分享 ...

最新文章

  1. “从客户端中检测到有潜在危险的 Request.Form 值“的解决方案汇总
  2. Git的分支命令详解
  3. SqlSession的使用范围---Mybatis学习笔记(五)
  4. SAP ABAP实用技巧介绍系列之 快速找出function module的帮助文档
  5. (47)Xilinx VIO IP核配置(八)(第10天)
  6. elastic search与postgresql的数据同步
  7. Linux进程间通信——使用消息队列
  8. C++中size_t和int区别
  9. 从 0 开始搭建一个技术博客,私藏干货~
  10. js 节点和checkbox 使用案例
  11. hdu3947 给一些已知(需费用)路径去覆盖一些边 //预先加灌法费用流
  12. system.gc()和system.runFinalization()区别作用
  13. deepinv2 添加打印机_科学网-Deepin Linux虚拟PDF打印机的安装与妙用-黄健的博文
  14. 08、ADS使用记录之低通滤波器设计与优化
  15. 图形B=B≌B凸显中学数学有一系列重大错误(更新稿) ——合同图形概念让5000年无人能识的自然数一下子浮出水面
  16. 纸壳CMS替换默认实现
  17. 重装系统后重装 mysql(windows)
  18. 【安全】Shellshock漏洞
  19. 仿微信的录制小视频功能
  20. 【解决方案】VS2013外部工具中添加ildasm.exe

热门文章

  1. Qt模态对话框/非模态对话框/半模态对话框
  2. android隐藏第三方应用图,第三方应用上架 帮你隐藏安卓手机的刘海
  3. 完全卸载nginx的详细步骤
  4. 专精特新“小巨人”企业申报条件和侧重点有哪些?
  5. Oracle数据库 —— DML完结
  6. A1097 Deduplication on a Linked List
  7. 文旅夜游如何焕发城市经济活力
  8. app提交上架最新流程 ios
  9. 阿里、腾讯、美团等P7岗位面试题
  10. IT专业大学生迷茫些什么