首页 > 编程语言 >java中的HashSet的add()方法解析

java中的HashSet的add()方法解析

时间:2023-01-02 23:13:19浏览次数:47  
标签:hash HashSet int return add key table java null

 HashSet的add()方法解析

示例代码如下:【可用于后续的源码追踪】

  1 public class HomeWork04 {
  2     public static void main(String[] args) {
  3         HashSet hashSet = new HashSet();
  4         // 重复元素的不重复添加
  5         hashSet.add("hello");
  6         hashSet.add("world");
  7         hashSet.add(new String("hello"));
  8         hashSet.add(new Person("tom",20));
  9         hashSet.add(new Person("jack",18));
 10         hashSet.add(new Person("tom",20)); // 重复元素【重写了hashCode和equals之后】
 11 
 12         // 超过阈值,hashSet如何扩容
 13         for(int i = 0;i<8;i++){
 14             hashSet.add(i);
 15         }
 16         hashSet.add(8);
 17 
 18         // 单链表个数超过8个,单链表树化
 19         for(int i = 0;i<9;i++){
 20             hashSet.add(new A(i));
 21         }
 22 
 23 
 24         // 遍历hashSet集合
 25         Iterator iterator = hashSet.iterator();
 26         while (iterator.hasNext()) {
 27             Object obj =  iterator.next();
 28             System.out.println(obj);
 29         }
 30 
 31         /**
 32          *  HashSet的无参构造器:
 33          *  public HashSet() {
 34          *      map = new HashMap<>(); // HashSet的底层还是HashMap【k-v】
 35          *  }
 36          *  $: 此时HashSet的table是[].
 37          *
 38          *  解析add()方法:
 39          *  1) public boolean add(E e) { ==> 【HashSet】
 40          *         // 这里的PRESENT是固定的
 41          *         return map.put(e, PRESENT)==null;
 42          *     }
 43          *    $:这里的add()方法,本质就是调用HashMap的put方法,存储key值
 44          *
 45          *  2) public V put(K key, V value) { ==> 【HashMap】
 46          *         return putVal(hash(key), key, value, false, true);
 47          *     }
 48          *    $:首先计算key的hash值,通过key的hash值,在table中存放key
 49          *
 50          *  3) static final int hash(Object key) { ==> 【HashMap】
 51          *         int h;
 52          *         return (key == null) ? 0 : (h = key.hashCode()) ^ (h >>> 16);
 53          *     }
 54          *    $:返回key的hash值
 55          *
 56          *  4) final V putVal(int hash, K key, V value, boolean onlyIfAbsent,
 57          *                    boolean evict) { ==> 【HashMap】
 58          *         Node<K,V>[] tab; Node<K,V> p; int n, i;
 59          *         if ((tab = table) == null || (n = tab.length) == 0)
 60          *             // 首次进入putVal(),table是null,为table扩容
 61          *             n = (tab = resize()).length;
 62          *         if ((p = tab[i = (n - 1) & hash]) == null)
 63          *             // 正式添加元素:
 64          *             // key求出的hash值经过某种算法,得到table处的索引i
 65          *             // 以key创建新Node节点,将该节点存放到table[i]处。
 66          *             tab[i] = newNode(hash, key, value, null);
 67          *         else {
 68          *             // table不为空,并且添加的key映射到table数组中的table[i]不为空【冲突】
 69          *             Node<K,V> e; K k;
 70          *             // p = table[i],这里p即为相同索引在table的内容
 71          *             // 比较p.key的hash值和key的hash值,同时比较p.key是否与key相同。
 72          *             if (p.hash == hash &&
 73          *                 ((k = p.key) == key || (key != null && key.equals(k))))
 74          *                 e = p;
 75          *             // 判断p是否是树节点
 76          *             else if (p instanceof TreeNode)
 77          *                 // p是树节点,则用putTreeVal()方式添加key
 78          *                 e = ((TreeNode<K,V>)p).putTreeVal(this, tab, hash, key, value);
 79          *             else {
 80          *             // p仍然是Node类的节点,遍历table[i]处的链表【单链表】
 81          *                 for (int binCount = 0; ; ++binCount) {
 82          *                     // 是否到达最后一个节点
 83          *                     if ((e = p.next) == null) {
 84          *                         // 最后一个节点,插入key
 85          *                         p.next = newNode(hash, key, value, null);
 86          *                         // 链表的个数大于8就要树化
 87          *                         if (binCount >= TREEIFY_THRESHOLD - 1) // -1 for 1st
 88          *                             treeifyBin(tab, hash);
 89          *                         ① break;
 90          *                     }
 91          *                     // 遍历过程中,判断当前节点e.hash值是否与key的hash值相同,并且e.key是否与key相同
 92          *                     if (e.hash == hash &&
 93          *                         ((k = e.key) == key || (key != null && key.equals(k))))
 94          *                         ② break;
 95          *                     // 移动p,通过e = p.next,再次移动e
 96          *                     // 通过对p.e的交替移动完成遍历。
 97          *                     p = e;
 98          *                 }
 99          *             }
100          *
101          *             // 将新Node节点的value替换掉旧节点的value【PRESENT不影响】
102          *             if (e != null) {
103          *                 V oldValue = e.value;
104          *                 if (!onlyIfAbsent || oldValue == null)
105          *                     e.value = value;
106          *                 afterNodeAccess(e);
107          *                 return oldValue;
108          *             }
109          *         }
110          *         // 记录不重复添加的个数
111          *         ++modCount;
112          *         if (++size > threshold)
113          *         // 如果当前的个数超过阈值,则需要扩容
114          *             resize();
115          *         afterNodeInsertion(evict);
116          *         return null;
117          *     }
118          *
119          *     $ oldTab:旧table数组,oldCap:旧table的容量,oldThr:旧table的阈值
120          *       newCap:新table的容量,newThr:新table的阈值,threshold:最终阈值
121          *   5) final Node<K,V>[] resize() { ==> 【HashMap】
122          *         Node<K,V>[] oldTab = table;
123          *         int oldCap = (oldTab == null) ? 0 : oldTab.length;
124          *         int oldThr = threshold;
125          *         int newCap, newThr = 0;
126          *         if (oldCap > 0) {
127          *             if (oldCap >= MAXIMUM_CAPACITY) {
128          *                 threshold = Integer.MAX_VALUE;
129          *                 return oldTab;
130          *             }
131          *             // 扩容机制:newCap:原数组长度的2倍,newThr:原阈值的2倍。
132          *             else if ((newCap = oldCap << 1) < MAXIMUM_CAPACITY &&
133          *                      oldCap >= DEFAULT_INITIAL_CAPACITY)
134          *                 newThr = oldThr << 1;
135          *         }
136          *         else if (oldThr > 0)
137          *             newCap = oldThr;
138          *         else {
139          *             // 首次oldCap为0,oldThr为0
140          *             // 为newCap赋初始值16,为newThr赋初始值0.75 * 16 = 12
141          *             newCap = DEFAULT_INITIAL_CAPACITY;
142          *             newThr = (int)(DEFAULT_LOAD_FACTOR * DEFAULT_INITIAL_CAPACITY);
143          *         }
144          *         if (newThr == 0) {
145          *             float ft = (float)newCap * loadFactor;
146          *             newThr = (newCap < MAXIMUM_CAPACITY && ft < (float)MAXIMUM_CAPACITY ?
147          *                       (int)ft : Integer.MAX_VALUE);
148          *         }
149          *         // threshold赋最终阈值
150          *         threshold = newThr;
151          *         // 按照newCap,为table分配存储空间
152          *         Node<K, V>[] newTab = (Node<K,V>[])new Node[newCap];
153          *         table = newTab;
154          *         // oldTab不为空的情况:【复制数据】
155          *         if (oldTab != null) {
156          *             // 复制旧数组的数据到新数组中
157          *             for (int j = 0; j < oldCap; ++j) {
158          *                 Node<K,V> e;
159          *                 if ((e = oldTab[j]) != null) {
160          *                     oldTab[j] = null;
161          *                     // 判断j索引的oldTab[j]是否是单个元素
162          *                     if (e.next == null)
163          *                         // 单个元素的话,计算索引值,存放到newTab中。
164          *                         newTab[e.hash & (newCap - 1)] = e;
165          *                     else if (e instanceof TreeNode)
166          *                         ((TreeNode<K,V>)e).split(this, newTab, j, oldCap);
167          *                     else {
168          *                     // j对应的oldTab[j]是单链表
169          *                         // 以下处理方式,尚未理解,仍需多看源码 ???
170          *                         Node<K,V> loHead = null, loTail = null;
171          *                         Node<K,V> hiHead = null, hiTail = null;
172          *                         Node<K,V> next;
173          *                         do {
174          *                             next = e.next;
175          *                             if ((e.hash & oldCap) == 0) {
176          *                                 if (loTail == null)
177          *                                     loHead = e;
178          *                                 else
179          *                                     loTail.next = e;
180          *                                 loTail = e;
181          *                             }
182          *                             else {
183          *                                 if (hiTail == null)
184          *                                     hiHead = e;
185          *                                 else
186          *                                     hiTail.next = e;
187          *                                 hiTail = e;
188          *                             }
189          *                         } while ((e = next) != null);
190          *                         if (loTail != null) {
191          *                             loTail.next = null;
192          *                             newTab[j] = loHead;
193          *                         }
194          *                         if (hiTail != null) {
195          *                             hiTail.next = null;
196          *                             newTab[j + oldCap] = hiHead;
197          *                         }
198          *                     }
199          *                 }
200          *             }
201          *         }
202          *         return newTab;
203          *     }
204          */
205     }
206 }
207 // 使得出现单链表
208 class A {
209     private int num;
210 
211     public A(int num) {
212         this.num = num;
213     }
214 
215     @Override
216     public int hashCode() {
217         return 100;
218     }
219 
220     @Override
221     public String toString() {
222         return "A{" +
223                 "num=" + num +
224                 '}';
225     }
226 }
227 
228 /**
229  * 自定义对象,重写hashCode()和equals()方法.
230  */
231 class Person {
232     private String name;
233     private int age;
234 
235     public Person(String name, int age) {
236         this.name = name;
237         this.age = age;
238     }
239 
240     @Override
241     public boolean equals(Object o) {
242         if (this == o) return true;
243         if (o == null || getClass() != o.getClass()) return false;
244         Person person = (Person) o;
245         return age == person.age && Objects.equals(name, person.name);
246     }
247 
248     /**
249      * 1) public static int hash(Object... values) {
250      *         return Arrays.hashCode(values);
251      *     }
252      * 2) public static int hashCode(Object a[]) {
253      *         if (a == null)
254      *             return 0;
255      *
256      *         int result = 1;
257      *
258      *         for (Object element : a)
259      *             result = 31 * result + (element == null ? 0 : element.hashCode());
260      *
261      *         return result;
262      *     }
263      * @return
264      */
265     @Override
266     public int hashCode() {
267         return Objects.hash(name, age);
268     }
269 
270     @Override
271     public String toString() {
272         return "Person{" +
273                 "name='" + name + '\'' +
274                 ", age=" + age +
275                 '}';
276     }
277 }

 

标签:hash,HashSet,int,return,add,key,table,java,null
From: https://www.cnblogs.com/zwgitOne123/p/17020553.html

相关文章

  • 注入——AddressOfEntryPoint入口点注入
    通过修改进程入口点,进行Shellcode注入。1、首先使用CreateProcess函数创建进程,并且将参数填写为CREATE_SUSPENDED这代表新进程的主线程被挂起了2、然后定位AddressOfEntr......
  • Javascript公共脚本库系列(二): 添…
    一.摘要本系列文章是为了抽象通用的,跨浏览器的脚本方法.本篇文章为我们的轻量级脚本库增加添加多播委托的方法.可以实现"单击页面空白区域则关闭页面上所有弹出层"等类似的......
  • java笔试题大全带答案(java笔试题大全带答案下载)
    谁有2008,2009,2010年计算机JAVA笔试试题和答案呢?2008年计算机Java笔试试题及答案2009年计算机考试Java笔试试题和答案2010年计算机考试Java笔试试题和答案下面地址也许有......
  • 第一章:Java开发环境搭建
    感谢动力节点(放弃幻想,认清现实)1.知识框架2.计算机编程语言发展史?第一代语言:机器语言程序员直接编写二进制,一串二进制代码,例如:10010100010010001000..........
  • Java个人家乡博客源码
    概述个人博客相册家乡主题,用户注册后可以发布关于家乡的特色文章介绍,可以发布照片,相册管理,留言,评论,回复,收藏,关注演示视频https://www.bilibili.com/video/BV1iy4y1x7w6......
  • Java开发的党员管理系统党员会议系统党务管理系统
    简介Java开发的大学生党员管理系统,主要功能会议,会议记录,会议主持,设置参会人员,请假申请,会议内容附件上传下载,党费管理,入党积极分子预备党员管理,人员变动,入党,表现评价等。主......
  • Java同学录系统同学录网站
    简介用户注册可以创建班级(创建者即为群主),用户也可以查找班级申请加入,群主添加同学的联系方式等,可以在班级里留言,管理相册等,还可以指定其他人为群主或者解散班级群,群里的用......
  • Java人脸识别相册分类按时间分类相册按城市分类相册app源码
    简介后台Java,前台mui开发的androidapp;主要是按拍摄时间,人脸,城市进行相册照片的分类。演示视频https://www.bilibili.com/video/BV1XP4y187rA/?share_source=copy_web&v......
  • java开发的美食菜谱网
    简介Java开发美食菜谱分享网站,健康专题分享,菜谱网源码。用户可以发布菜谱,可以评论,可以发布关于健康饮食的文章,还可以查看自己的主页。管理员可以管理所有的数据,审核数据。......
  • Java【双色球案例】
    题目:1.投注是从红色球号码中选择6个号码(不能重复),从蓝色球号码中选择1个号码,组合为一注投注号码的投注2.红色球号码为[1-33],蓝色球号码为[1-16]3.开奖号码和购彩者号码......