首页 > 编程语言 >java学习日记20230414-HashSet源码

java学习日记20230414-HashSet源码

时间:2023-04-18 22:37:26浏览次数:36  
标签:java HashSet int 源码 key return null public hash

HashSet

  • HashSet底层是HashMap
  • 添加一个元素时,先得到Hash值,会转化成索引值;
  • 找到存储数据表table,看这个索引位置是否存放元素;
  • 如果没有直接加入
  • 如果有,调用equals比较,如果相同放弃添加,如果不同,则添加到最后
  • 在java8中,如果一条链表的元素个数到达TREEIFY_THRESHOLD(默认是8)(table表扩容),并且table的大小>=MIN_TREEIFY_CAPACITY(64),就会进行树化——红黑树
  • 原理:
    • 先获取元素的哈希值
    • 对哈希值进行运算,得到索引值,即存放在哈希表中的位置号
    • 如果该位置没有元素,则添加,如果有,则需要通过该元素的equals方法进行判断,相等不添加,不想等,以链表的方式添加
    • public class HashSetSource {
          public static void main(String[] args) {
      
              /**
               *
               *     public HashSet() {
               *         map = new HashMap<>();
               *     }
               *
               *     public boolean add(E e) {
               *         return map.put(e, PRESENT)==null;//private static final Object PRESENT = new Object();
               *     }
               *     public V put(K key, V value) {
               *         return putVal(hash(key), key, value, false, true);
               *     }
               *
               *     static final int hash(Object key) {
               *         int h;
               *         return (key == null) ? 0 : (h = key.hashCode()) ^ (h >>> 16);
               *     }
               *
               *     final V putVal(int hash, K key, V value, boolean onlyIfAbsent,
               *                    boolean evict) {
               *         Node<K,V>[] tab; Node<K,V> p; int n, i;//定义辅助变量
               *         //table是hashmap的一个数组,类型是Node[]
               *         //if语句表示如果当前table为null,或者大小为0,就进行第一次扩容,16个空间
               *         if ((tab = table) == null || (n = tab.length) == 0)
               *             n = (tab = resize()).length;
               *         //根据key得到hash,去计算key应该存放到table表的索引位置
               *         //并把对象赋给p
               *         //判断p是否为null,如果为null,表示没有存放元素,就创建一个Node
               *         //就放在该位置
               *         if ((p = tab[i = (n - 1) & hash]) == null)
               *             tab[i] = newNode(hash, key, value, null);
               *         else {
               *             Node<K,V> e; K k;//辅助变量,代码块定义
               *             //如果当前索引位置对应的链表的第一个元素和准备添加的key的hash值一样 p.hash ==hash
               *             //(1)并且满足准备加入的key和p指向的node结点的对象是同一个对象
               *             //(2)或者p指向的Node结点的key的equals方法和准备加入的key比较后,相同(equeals是添加对象的equals方法,由程序员选择)
               *             // 则认为是相同对象,无法进行添加
               *             if (p.hash == hash &&
               *                 ((k = p.key) == key || (key != null && key.equals(k))))
               *                 e = p;
               *             //再判断p是不是红黑树
               *             //如果是红黑树,则调用putTreeVal进行添加
               *             else if (p instanceof TreeNode)
               *                 e = ((TreeNode<K,V>)p).putTreeVal(this, tab, hash, key, value);
               *             else {
               *                  //table对应的索引是一个链表
               *                  //依次跟该链表的每隔元素比较
               *                  //比较Node结点是否存在该对象,如果存在则不进行添加,如果不存在则添加到最后
               *                  //在添加后立即判断链表是否到达8个结点,如果达到调用treeifyBin()对当前链表进行树化
               *                  //在进行树化时,进行判断,table的size小于64,对表进行扩容,大于64则进行树化
               *                  // if (tab == null || (n = tab.length) < MIN_TREEIFY_CAPACITY)
               *                  // resize();
               *                 for (int binCount = 0; ; ++binCount) {
               *                     if ((e = p.next) == null) {
               *                         p.next = newNode(hash, key, value, null);
               *                         if (binCount >= TREEIFY_THRESHOLD - 1) // -1 for 1st
               *                             treeifyBin(tab, hash);
               *                         break;
               *                     }
               *                     if (e.hash == hash &&
               *                         ((k = e.key) == key || (key != null && key.equals(k))))
               *                         break;
               *                     p = e;
               *                 }
               *             }
               *             if (e != null) { // existing mapping for key
               *                 V oldValue = e.value;
               *                 if (!onlyIfAbsent || oldValue == null)
               *                     e.value = value;
               *                 afterNodeAccess(e);
               *                 return oldValue;
               *             }
               *         }
               *         ++modCount;
               *         if (++size > threshold)
               *             resize();
               *         afterNodeInsertion(evict);
               *         return null;
               *     }
               */
              HashSet hashSet = new HashSet();
              hashSet.add("java");
              hashSet.add("php");
              hashSet.add("java");
              System.out.println("hashSet="+hashSet);
          }

      HashSet底层机制说明:

      • HashSet底层时HashMap,第一次添加时,table数组扩容到16,临界值threshold是16*加载因子loadFactory0.75
      • 如果table数组使用到了临界值就会自动触发扩容16*2=32个,新的临界值时32*0.75=24
      • 在java8中,如果一条链表的元素个数到达TREEIFY_THRESHOLD默认是8并且table的大小>=MIN_TREEIFY_CAPACITY(默认是64),就会进行红黑树,否则任然采用数组扩容。
      • 无论是链表还是数组,每新增一个元素,都会新增一个节点,都会计算到临界值中
    • package com.study.set_;
      
      import java.util.HashSet;
      import java.util.Objects;
      
      /**
       * @author jay
       * @version 1.0
       * @date 2023/4/18
       */
      public class HashSetHomework {
          public static void main(String[] args) {
              HashSet hashSet = new HashSet();
              hashSet.add(new EmployeeHome("Jack",200,new MyDate(2022,10,10)));
              hashSet.add(new EmployeeHome("Jack",200,new MyDate(2022,10,10)));
              hashSet.add(new EmployeeHome("Jack",200,new MyDate(2022,10,20)));
              System.out.println(hashSet);
          }
      }
      
      
      class EmployeeHome{
          @Override
          public String toString() {
              return "EmployeeHome{" +
                      "name='" + name + '\'' +
                      ", sal=" + sal +
                      ", birthDay=" + birthDay +
                      '}';
          }
      
          private String name;
          private double sal;
          private MyDate birthDay;
      
          public EmployeeHome(String name, double sal, MyDate birthDay) {
              this.name = name;
              this.sal = sal;
              this.birthDay = birthDay;
          }
      
          @Override
          public boolean equals(Object o) {
              if (this == o) return true;
              if (o == null || getClass() != o.getClass()) return false;
              EmployeeHome that = (EmployeeHome) o;
              return Objects.equals(name, that.name) && Objects.equals(birthDay, that.birthDay);
          }
      
          @Override
          public int hashCode() {
              return Objects.hash(name, birthDay);
          }
      }
      
      class MyDate{
          private int year;
          private int month;
          private int day;
      
          public MyDate(int year, int month, int day) {
              this.year = year;
              this.month = month;
              this.day = day;
          }
      
          public int getYear() {
              return year;
          }
      
          public void setYear(int year) {
              this.year = year;
          }
      
          public int getMonth() {
              return month;
          }
      
          public void setMonth(int month) {
              this.month = month;
          }
      
          public int getDay() {
              return day;
          }
      
          public void setDay(int day) {
              this.day = day;
          }
      
          @Override
          public boolean equals(Object o) {
              if (this == o) return true;
              if (o == null || getClass() != o.getClass()) return false;
              MyDate myDate = (MyDate) o;
              return year == myDate.year && month == myDate.month && day == myDate.day;
          }
      
          @Override
          public int hashCode() {
              return Objects.hash(year, month, day);
          }
      
          @Override
          public String toString() {
              return "MyDate{" +
                      "year=" + year +
                      ", month=" + month +
                      ", day=" + day +
                      '}';
          }
      }

       

标签:java,HashSet,int,源码,key,return,null,public,hash
From: https://www.cnblogs.com/DragonJack/p/17317126.html

相关文章

  • JavaScript程序与设计入门到入土
    4.JavaScript代码的书写位置和css一样,我们的js也可以有多种方式书写在页面上让其生效js也有多种方式书写,分为行内式,内嵌式,外链式4-1行内式JS代码(不推荐)写在标签上的js代码需要依靠事件(行为)来触发<!--写在a标签的href属性上--><ahref="javascript:alert('我是......
  • 源码共读|yocto-queue 队列 链表
    前言Yocto-queue是一种允许高效存储和检索数据的数据结构。它是一种队列类型,是一个元素集合,其中的项被添加到一端并从另一端移除。它被设计用来操作数据量很大的数组,在你需要使用大量的Array.push、Array.shift操作时,Yocto-queue有更好的性能表现。仓库地址:sindresorhus/yo......
  • 【Java】构造方法
    如果想在创建对象时就能完成属性的初始化操作,给属性赋相应的值,可通过类的特殊成员——构造方法(也称为构造函数)完成。构造方法可用于当对象被创建时初始化对象中的属性。构造方法时一个特殊的方法,它的名字必须与所在的类的名字相同,且没有返回类型。语法:【访问符】<类名>(【参数列......
  • java异常处理
    Java异常处理异常是程序中的一些错误,但并不是所有的错误都是异常,并且错误有时候是可以避免的。比如说,你的代码少了一个分号,那么运行出来结果是提示是错误java.lang.Error;如果你用System.out.println(11/0),那么你是因为你用0做了除数,会抛出java.lang.ArithmeticException的异......
  • 【Java技术指南】「Unirest编程专题」一起认识一下一个“灰常”优秀的Http工具,让Http
    Unirest-Java是一个轻量级的HTTP客户端库,它提供了简单易用的API,可以帮助Java开发人员快速地发送HTTP请求和处理响应。在本文中,我们将深入探讨Unirest-Java的技术细节和使用方法。Unirest-Java的优点简单易用:Unirest-Java提供了一组简单易用的API,可以帮助Java开发人员快速地发送HTTP......
  • 【Python毕业设计】基于Python+Flask+MySQL的学生信息管理系统(附完整源码)
    1、项目说明基于python+Flask+mysql的学生信息管理系统项目实战项目需要安装pycharm专业版,mysql数据库以及项目所需的所有模块创建数据库名称db_online_notes,然后执行sql文件生成数据表和数据项目需要安装flask,pymysql以及其他的一些模块安装命令如下:pipinstall-ihttps://......
  • vue2源码-八、依赖收集的过程
    依赖收集的过程前言使用真实节点替换原始节点,主要涉及以下步骤:1.新老节点的更新方案。2.虚拟节点与真实节点映射。3.实现新老节点的替换。依赖收集已经完成了Vue的两大核心部分:响应式数据和数据渲染,即完成了整个Vue的初始化流程:当newVue()时,执行_init初始化,通过moun......
  • Java异常
    一、理论部分1、Java异常架构与异常关键字Java异常简介Java异常是Java提供的一种识别及响应错误的一致性机制。Java异常机制可以使程序中异常处理代码和正常业务代码分离,保证程序代码更加优雅,并提高程序健壮性。在有效使用异常的情况下,异常能清晰的回答what,where,why这3个......
  • OpenHarmony源码解析之系统服务管理子系统
    1预备知识Linux中主要的IPC机制有:管道(pipe)、信号(signal)、信号量(semophore)、消息队列(Message)、共享内存(ShareMemory)、套接字(Socket)等。OpenHarmony基于binder驱动封装了一套ipc机制(foundation\communication\ipc)用于实现设备内的跨进程通信。Binder机制通常采用客户端-服务器(Cli......
  • java实现自动售货机
    自动售货机主要实现两大功能:售卖和管理。一、对于售卖,购买者只要可以看见有什么卖,卖多少钱就可以。先给售货机一个初始的商品列表。在储存商品时我用的是链表写的,方便遍历。publicvoidinitProduct(){ producta=newproduct();a.setID(1);a.setName("......