此类实现 Set 接口,由哈希表(实际上是一个 HashMap 实例)支持。它不保证 set 的迭代顺序;特别是它不保证该顺序恒久不变。此类允许使用 null 元素。

HashSet<String> set = new HashSet<String>();set.add("abc");

 1      private transient HashMap<E,Object> map;
 2     /**
 7      * Constructs a new, empty set; the backing <tt>HashMap</tt> instance has
 8      * default initial capacity (16) and load factor (0.75).
 9      */
10     public HashSet() {
11         map = new HashMap<>();
12     }

点击

HashSet

进入 看Hash源码,证明 它确实是 由一个 HashMap 实例支持。

众所周知,set是无序,不重复的。那么它是如何保证元素唯一性的呢?

先看源码。点击 add方法进入。

public boolean add(E e) {return map.put(e, PRESENT)==null;}

public V put(K key, V value) {return putVal(hash(key), key, value, false, true);}

 1 final V putVal(int hash, K key, V value, boolean onlyIfAbsent,
 2                    boolean evict) {
 3         Node<K,V>[] tab; Node<K,V> p; int n, i;
 4         if ((tab = table) == null || (n = tab.length) == 0)
 5             n = (tab = resize()).length;
 6         if ((p = tab[i = (n - 1) & hash]) == null)
 7             tab[i] = newNode(hash, key, value, null);
 8         else {
 9             Node<K,V> e; K k;
10             if (p.hash == hash &&
11                 ((k = p.key) == key || (key != null && key.equals(k))))
12                 e = p;
13             else if (p instanceof TreeNode)
14                 e = ((TreeNode<K,V>)p).putTreeVal(this, tab, hash, key, value);
15             else {
16                 for (int binCount = 0; ; ++binCount) {
17                     if ((e = p.next) == null) {
18                         p.next = newNode(hash, key, value, null);
19                         if (binCount >= TREEIFY_THRESHOLD - 1) // -1 for 1st
20                             treeifyBin(tab, hash);
21                         break;
22                     }
23                     if (e.hash == hash &&
24                         ((k = e.key) == key || (key != null && key.equals(k))))
25                         break;
26                     p = e;
27                 }
28             }
29             if (e != null) { // existing mapping for key
30                 V oldValue = e.value;
31                 if (!onlyIfAbsent || oldValue == null)
32                     e.value = value;
33                 afterNodeAccess(e);
34                 return oldValue;
35             }
36         }
37         ++modCount;
38         if (++size > threshold)
39             resize();
40         afterNodeInsertion(evict);
41         return null;
42     }

1 static final int hash(Object key) {
2         int h;
3         return (key == null) ? 0 : (h = key.hashCode()) ^ (h >>> 16);
4     }

通过以上源码,可以发现为保证唯一性。

1.将传入的元素进行hashCode方法调用,得到该元素的hash值。拿到hash值还需要和数组的长度进行运算,获取元素存储的下标值。

获取元素存储的下标值,尝试将传入的元素存储到对应的下标中。

2.如果计算出来的下标中,不存在元素,则直接存储。否则执行第3步的equals方法。

3.如果存储对象的equals方法返回true,说明是一样的,所以不存。如果返回false,说明不一样,要存储起来。

4.使用“单链表”将存储数据链接起来。

那么单链表是什么样子的代码呢?

Node<K,V> next; 这就是单链表的数据结构
 1  static class Node<K,V> implements Map.Entry<K,V> {
 2         final int hash;
 3         final K key;
 4         V value;
 5         Node<K,V> next;
 6
 7         Node(int hash, K key, V value, Node<K,V> next) {
 8             this.hash = hash;
 9             this.key = key;
10             this.value = value;
11             this.next = next;
12         }
13
14         public final K getKey()        { return key; }
15         public final V getValue()      { return value; }
16         public final String toString() { return key + "=" + value; }
17
18         public final int hashCode() {
19             return Objects.hashCode(key) ^ Objects.hashCode(value);
20         }
21
22         public final V setValue(V newValue) {
23             V oldValue = value;
24             value = newValue;
25             return oldValue;
26         }
27
28         public final boolean equals(Object o) {
29             if (o == this)
30                 return true;
31             if (o instanceof Map.Entry) {
32                 Map.Entry<?,?> e = (Map.Entry<?,?>)o;
33                 if (Objects.equals(key, e.getKey()) &&
34                     Objects.equals(value, e.getValue()))
35                     return true;
36             }
37             return false;
38         }
39     }

这个就是hashSet的存储图。其中红色的线就是链表线。

这个单链表具体长这样的:

最后结论:HashSet底层依赖HashMap来实现。使用Node数组与单链表来实现元素的存储。

ps:听说jdk1.8以后当单链表大于8的长度时,会添加红黑树来实现。

转载于:https://www.cnblogs.com/daiwei1981/p/9087473.html

HashSet底层存储元素的源码分析相关推荐

  1. redis底层数据结构(redis底层存储结构、源码分析)

    文章目录 前言 一.redis为什么快? 二.redis的底层数据结构 2.1.redis的底层存储的扩容机制 2.1.1.扩容时间 2.1.2.扩容多大 2.1.3.扩容后的rehash 2.1.4 ...

  2. ECharts 3.0底层zrender 3.x源码分析1-总体架构

    zrender是一个轻量级的Canvas类库,作为百度Echarts 3.0的底层基础.截至目前查看的zrender源码和文档,包括官网文档都还停留在2.x时代,我打算用一个系列介绍下zrender ...

  3. java类加载机制为什么双亲委派_[五]类加载机制双亲委派机制 底层代码实现原理 源码分析 java类加载双亲委派机制是如何实现的...

    Launcher启动类 本文是双亲委派机制的源码分析部分,类加载机制中的双亲委派模型对于jvm的稳定运行是非常重要的不过源码其实比较简单,接下来简单介绍一下我们先从启动类说起有一个Launcher类 ...

  4. concurrent 底层_JDK1.8 util-concurrent-ConcurrentLinkedQueue源码分析

    1.ConcurrentLinkedQueue简介 在单线程编程中我们会经常用到一些集合类,比如ArrayList,HashMap等,但是这些类都不是线程安全的类.在面试中也经常会有一些考点,比如Ar ...

  5. HashMap、ConcurretnHashMap面试题详解,源码分析

    文章目录 面试题 HashMap.LinkedHashMap和TreeMap的区别是什么? ①:为什么hashmap每次扩容大小为2的n次方? ③:jdk1.7的hashmap的扩容操作是在元素插入之 ...

  6. 史上超级详细:HashMap源码分析,你了解到源码的魅力了嘛

    ##HashMap1.8和1.8之前的源码差别很大 目录 简介 数据结构 类结构 属性 构造方法 增加 1.HashMap简介 HashMap基于哈希表的Map接口实现,是以key-value存储形式 ...

  7. HashMap源码分析(超级详细)

    1.HashMap简介 HashMap基于哈希表的Map接口实现,是以key-value存储形式存在.(除了不同步和允许使用 null 之外,HashMap 类与 Hashtable 大致相同.) 1 ...

  8. Vue.js 源码分析(二十三) 指令篇 v-show指令详解

    v-show的作用是将表达式值转换为布尔值,根据该布尔值的真假来显示/隐藏切换元素,它是通过切换元素的display这个css属性值来实现的,例如: <!DOCTYPE html> < ...

  9. vue源码分析系列三:render的执行过程和Virtual DOM的产生

    render 手写 render 函数,仔细观察下面这段代码,试想一下这里的 createElement 参数是什么 . new Vue({el: '#application',render(crea ...

  10. HashSet源码分析

    HashSet底层是HashMap实现的,关于HashMap的分析请移步到HashMap源码分析 属性 //底层使用HashMap来实现 private transient HashMap<E, ...

最新文章

  1. 微信团队放大招!他们为了小程序居然...
  2. python实训第七天
  3. 全球最大保险公司之一Ace推出一亿美元网络安全险
  4. [hackinglab][CTF][注入关][2020] hackinglab 注入关 writeup
  5. Jsoup使用选择器语法来查找元素
  6. python3基础知识三
  7. Gstreamer衬垫(pad)支持的媒体类型(三)
  8. Hive报错:Unable to instantiate org.apache.hadoop.hive.ql.metadata.SessionHiveMetaStoreClient
  9. matlab 不同函数间传递结构体数据_VC与Matlab混合编程及复杂数据:结构体传递
  10. 科学研究设计一:什么是科学
  11. Deecamp2019年试题A卷详解和感受
  12. 掘金 AMA:听蚂蚁金服高级技术专家-- 章耿谈微服务、架构、日志那些事
  13. html网页制作比赛要求,校园网页设计大赛活动方案
  14. 数分下(第1讲):一阶微分方程的三类模型求解
  15. Incremental Graph Convolutional Network for Collaborative Filtering(阅读论文笔记)
  16. hht去噪matlab实现,hht语音去噪 代码运行速度慢 程序报错 能帮我看下嘛
  17. Intent的用法(初步)
  18. Doctype 作用? 严格模式与混杂模式-如何触发这两种模式,区分它们有何意义?
  19. DHCP:(5)华为防火墙USG上部署DHCP服务以及DHCP中继
  20. pg数据库和mysql8_MySQL8与PG10:新版本下的较量谁更胜一筹?

热门文章

  1. codeforces 675C C. Money Transfers(贪心)
  2. 《单词的减法》state1~state17(第一遍学习记录)
  3. 为Android GridView 设置行背景
  4. iBATIS In Action:iBATIS的安装和配置
  5. 【MM 发票】MM MIRO界面中的ITEM LIST的布局可自己定义(转)
  6. php 数组任意位置插入值
  7. Lucene 的 Scoring 评分机制
  8. ASP.NET MVC+EF框架+EasyUI实现权限管理系列(23)-设置角色遗留问题和为权限设置角色以及EasyUI Tabs的使用...
  9. Jackrabbit介绍
  10. python的Singleton模式实现