10.1 HashSet 的全面说明

package com.xjz.set_;import java.util.HashSet;
import java.util.Set;@SuppressWarnings({"all"})
public class HashSet_ {public static void main(String[] args) {//1. 构造器走的源码/*public HashSet() {map = new HashMap<>();}2/ HashSet 可以存放 null,但是只能有一个null,即元素不能重复*/Set hashSet = new HashSet();hashSet.add(null);hashSet.add(null);System.out.println("hashSet=" + hashSet);}
}

10.2 HashSet 案例说明

package com.xjz.set_;import java.util.HashSet;@SuppressWarnings({"all"})
public class HashSet01 {public static void main(String[] args) {HashSet set = new HashSet();//说明//1. 在执行 add方法后,会返回一个boolean值//2. 如果添加成功,返回 true,否则返回fasle//3. 可以通过 remove 指定删除哪个对象System.out.println(set.add("john"));//TSystem.out.println(set.add("lucy"));//TSystem.out.println(set.add("john"));//FSystem.out.println(set.add("jack"));//TSystem.out.println(set.add("Rose"));//Tset.remove("john");System.out.println("set=" + set); // 3个set = new HashSet();System.out.println("set=" + set); // 0个//4 Hashset 不能添加相同的元素/数据?set.add("lucy");//添加成功set.add("lucy");//加入不了set.add(new Dog("tom"));//OKset.add(new Dog("tom"));//OKSystem.out.println("set=" + set);//在加深一下,非常经典的面试题//看源码,做分析//去看它的源码,即 add 到底发生了什么?==》底层机制set.add(new String("qdy"));//okset.add(new String("qdy"));//加入不了System.out.println("set=" + set);}
}
class Dog{  //定义了 Dog 类private String name;public Dog(String name) {this.name = name;}@Overridepublic String toString() {return "Dog{" +"name='" + name + '\'' +'}';}
}

10.3 HashSet 底层机制说明

HashSetStructure.java

package com.xjz.set_;@SuppressWarnings({"all"})
public class HashSetStructure {public static void main(String[] args) {//模拟一个HashSet的底层(HashMap 的底层结构)//1. 创建一个数组,数组的类型是 Node[]//2. 有些人,直接把 Node[] 数组成为  表Node[] table = new Node[16];System.out.println("table=" + table);//3. 创建结点Node john = new Node("john", null);table[2] = john; // 把 john 放到 table 表的索引为2的位置上Node jack = new Node("jack", null);john.next = jack; // 将jack 结点挂载到johnNode rose = new Node("Rose", null);jack.next = rose; // 将rose 结点挂载到jackNode lucy = new Node("lucy", null);table[3] = lucy; // 把lucy 放到 table 表的索引为3的位置System.out.println("table=" + table);}
}class Node { //结点,存储数据,可以指向下一个结点,从而形成链表Object item; //存放数据Node next; // 指向下一个结点//有参构造器public Node(Object item, Node next) {this.item = item;this.next = next;}
}

HashSetSource.java

package com.xjz.set_;import java.util.HashSet;@SuppressWarnings({"all"})
public class HashSetSource {public static void main(String[] args) {HashSet hashSet = new HashSet();hashSet.add("java"); //到此位置,第1次 add 分析完毕.hashSet.add("php");  //到此位置,第2次 add 分析完毕.hashSet.add("java");System.out.println("set=" + hashSet);/*对 HashSet 的源码解读1. 执行 HashSet()public HashSet() {map = new HashMap<>();}2. 执行 add()public boolean add(E e) {  // e = "java"return map.put(e, PRESENT)==null; //(static) PRESENT = new Object();}3. 执行 put(),该方法会执行 hash(key) 得到 key 对应的 hash 值,算法 h=key.hashCode())^(h>>>16)public V put(K key, V value) { //key = "java" value = PRESENT 共享return putVal(hash(key), key, value, false, true);}4. 执行 putVal()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;//(1)根据 key,得到 hash 去计算该 key 应该存放到 table 表的哪个索引位置//并把这个位置的对象,赋给p//(2)判断 p 是否为null//(2.1) 如果 p 为 null,表示还没有存放元素,则创建了一个 Node(key="java",value=PRESENT)//(2.2) 就放在该位置 tab[i] = newNode(hash,key,value,null)if ((p = tab[i = (n - 1) & hash]) == null)tab[i] = newNode(hash, key, value, null);else {//一个开发技巧提示:在需要局部变量(辅助变量)时候,再创建它Node<K,V> e; K k;//如果当前索引位置对应的链表的第一个元素和准备添加的 key 的 hash 值一样//并且满足 下面两个条件之一://(1) 准备加入的 key 和 p 指向的 Node 结点的 key 是同一个对象//(2) p 指向的 Node 结点的 key 的 equals() 和准备加入的 key 比较后相同// 就不能加入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 对应索引位置,已经是一个链表,就使用 for 循环比较//(1) 依次和该链表的每一个元素比较后,都不相同,则加入到该链表的最后//    注意在把元素添加到链表后,立即判断 该链表是否已经达到 8 个结点//    ,就调用 treeifyBin() 对当前这个链表进行树化(转成红黑树)//    注意,在转成红黑树时,要进行判断,判断条件//    if (tab == null || (n = tab.length) < MIN_TREEIFY_CAPACITY(64))//          resize();//    如果上面条件成立,先 table 扩容//    只有上面条件不成立时,才进行转成红黑树//(2) 依次和该链表的每一个元素比较过程中,如果有相同情况,就直接 breakfor (int binCount = 0; ; ++binCount) {if ((e = p.next) == null) {p.next = newNode(hash, key, value, null);if (binCount >= TREEIFY_THRESHOLD - 1) // -1 for 1sttreeifyBin(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 keyV oldValue = e.value;if (!onlyIfAbsent || oldValue == null)e.value = value;afterNodeAccess(e);return oldValue;}}++modCount;// size 就是我们每加入一个结点 Node(k,v,h,next),size++if (++size > threshold)resize(); // 扩容afterNodeInsertion(evict);return null;}*/}
}

HashSetIncrement.java

package com.xjz.set_;import java.util.HashSet;
import java.util.Objects;@SuppressWarnings({"all"})
public class HashSetIncrement {public static void main(String[] args) {/*HashSet 底层是 HashMap, 第一次添加时,table 数组扩容到 16,临界值(threshold)是 16*加载因子(loadFactor)是 0.75 = 12如果 table 数组使用到了临界值 12,就会扩容到 16 * 2 = 32,新的临界值就是 32*0.75 = 24, 依次类推*/HashSet hashSet = new HashSet();
//            for(int i = 1;i <= 100; i++){//                hashSet.add(i); //1,2,3,4,5....100
//            }/*在 Java8 中, 如果一条链表的元素个数到达 TREEIFY_THRESHOLD(默认是 8 ),并且 table 的大小 >= MIN_TREEIFY_CAPACITY(默认 64),就会进行树化(红黑树),否则仍然采用数组扩容机制*///            for(int i = 1; i <= 12; i++){//                hashSet.add(new A(i)); //
//            }/*当我们向 hashset 增加一个元素,-> Node -> 加入 table , 就算是增加了一个 size++*/for (int i = 1;i <= 7; i++){ //在 table 的某一条链表上添加了 7 个 A 对象hashSet.add(new A(i));}for (int i = 1;i <=7; i++){ //在 table 的某一条链表上添加了 7 个 B 对象hashSet.add(new B(i));}}
}
class B{private int n;public B(int n) {this.n = n;}@Overridepublic int hashCode() {return 200;}
}
class A{private int n;public A(int n) {this.n = n;}@Overridepublic int hashCode() {return 100;}
}

10.4 HashSet 课堂练习1

HashSetExercise.java

package com.xjz.set_;import java.util.HashSet;
import java.util.Objects;@SuppressWarnings({"all"})
public class HashSetExercise {public static void main(String[] args) {/**定义一个 Employee 类,该类包含:private 成员属性 name,age 要求:创建 3 个 Employee 对象放入 HashSet 中当 name 和 age 的值相同时,认为是相同员工, 不能添加到 HashSet 集合中*/HashSet hashSet = new HashSet();hashSet.add(new Employee("milan",18));//okhashSet.add(new Employee("smith",28));//okhashSet.add(new Employee("milan",18));//加入不成功//回答,如果不重写equals方法 加入了几个? 3个System.out.println("hashSet=" + hashSet);}
}
//创建 Employee
class Employee {private String name;private int age;public Employee(String name, int age) {this.name = name;this.age = age;}public String getName() {return name;}public void setName(String name) {this.name = name;}public int getAge() {return age;}public void setAge(int age) {this.age = age;}@Overridepublic String toString() {return "Employee{" +"name='" + name + '\'' +", age=" + age +'}';}@Overridepublic boolean equals(Object o) {if (this == o) return true;if (o == null || getClass() != o.getClass()) return false;Employee employee = (Employee) o;return age == employee.age &&Objects.equals(name, employee.name);}@Overridepublic int hashCode() {return Objects.hash(name, age);}
}

10.5 HashSet 课后练习2


只需要把 Employee01 和 MyDate 两个类都重写一下 equals( ) 和 hashCode( ) 方法,就不会有相同员工进入

package com.xjz.set_;import java.util.HashSet;
import java.util.Objects;@SuppressWarnings({"all"})
public class HashSetExercise02 {public static void main(String[] args) {//只需要把 Employee01 和 MyDate 两个类都重写一下 equals() 和 hashCode() 方法,就不会有相同员工进入HashSet hashSet = new HashSet();hashSet.add(new Employee01("秋刀鱼", 19, new MyDate(2002, 12, 02)));hashSet.add(new Employee01("苏炳添", 32, new MyDate(1989, 04, 21)));hashSet.add(new Employee01("秋刀鱼", 19, new MyDate(2002, 12, 02)));System.out.println("hashSet=" + hashSet);}
}//创建Employee类
class Employee01 {private String name;private int sal; //薪水private MyDate birthday;public Employee01(String name, int sal, MyDate birthday) {this.name = name;this.sal = sal;this.birthday = birthday;}public String getName() {return name;}public void setName(String name) {this.name = name;}public int getSal() {return sal;}public void setSal(int sal) {this.sal = sal;}public MyDate getBirthday() {return birthday;}public void setBirthday(MyDate birthday) {this.birthday = birthday;}@Overridepublic String toString() {return "Employee01{" +"name='" + name + '\'' +", sal=" + sal +", birthday=" + birthday +'}';}@Overridepublic boolean equals(Object o) {if (this == o) return true;if (o == null || getClass() != o.getClass()) return false;Employee01 that = (Employee01) o;return sal == that.sal &&Objects.equals(name, that.name) &&Objects.equals(birthday, that.birthday);}@Overridepublic int hashCode() {return Objects.hash(name, sal, 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;}@Overridepublic String toString() {return "MyDate{" +"year=" + year +", month=" + month +", day=" + day +'}';}@Overridepublic 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;}@Overridepublic int hashCode() {return Objects.hash(year, month, day);}
}package com.xjz.set_;import java.util.HashSet;
import java.util.Objects;@SuppressWarnings({"all"})
public class HashSetExercise02 {public static void main(String[] args) {//只需要把 Employee01 和 MyDate 两个类都重写一下,就不会有相同员工进入HashSet hashSet = new HashSet();hashSet.add(new Employee01("秋刀鱼", 19, new MyDate(2002, 12, 02)));hashSet.add(new Employee01("苏炳添", 32, new MyDate(1989, 04, 21)));hashSet.add(new Employee01("秋刀鱼", 19, new MyDate(2002, 12, 02)));System.out.println("hashSet=" + hashSet);}
}//创建Employee类
class Employee01 {private String name;private int sal; //薪水private MyDate birthday;public Employee01(String name, int sal, MyDate birthday) {this.name = name;this.sal = sal;this.birthday = birthday;}public String getName() {return name;}public void setName(String name) {this.name = name;}public int getSal() {return sal;}public void setSal(int sal) {this.sal = sal;}public MyDate getBirthday() {return birthday;}public void setBirthday(MyDate birthday) {this.birthday = birthday;}@Overridepublic String toString() {return "Employee01{" +"name='" + name + '\'' +", sal=" + sal +", birthday=" + birthday +'}';}@Overridepublic boolean equals(Object o) {if (this == o) return true;if (o == null || getClass() != o.getClass()) return false;Employee01 that = (Employee01) o;return sal == that.sal &&Objects.equals(name, that.name) &&Objects.equals(birthday, that.birthday);}@Overridepublic int hashCode() {return Objects.hash(name, sal, 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;}@Overridepublic String toString() {return "MyDate{" +"year=" + year +", month=" + month +", day=" + day +'}';}@Overridepublic 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;}@Overridepublic int hashCode() {return Objects.hash(year, month, day);}
}

Set 接口实现类-HashSet相关推荐

  1. 【Java8】堆栈/队列/数组/链表/红黑树,List/set子接口,hashcode/hashset,Map/内部接口,/统计字符个数,debug,斗地主,Collections,TreeSet

    文章目录 1.堆栈/队列/数组/链表:数据结构即计算机组织管理数据的方式,堆栈指的是内存图中的栈,不是堆 2.红黑树:二查,二查平,二查平1倍 3.List子接口:集合,IndexOutOfBound ...

  2. 《JAVA 进阶: Collection子接口Set(HashSet,LinkedHashSet,TreeSet)》

    一.Set接口 Set接口是Collection的子接口,set接口没有提供额外的方法 Set 集合不允许包含相同的元素,如果试把两个相同的元素加入同一个 Set 集合中,则添加操作失败 Set 判断 ...

  3. Servlet基础:接口、类、请求响应、配置、会话追踪、上下文、协作、异常

    10.1 Servlet介绍 ​ Servlet技术是Sun公司提供的一种实现动态网页的解决方案,它是基于Java编程语言的Web服务器端编程技术,主要用于在Web服务器端获得客户端的访问请求信息和动 ...

  4. 接口、类、抽象类、对象的另类解释

    大家也许都知道做工艺器或是工厂里做生产某些产品的模具模具.如做一个金属的五角星,只要将钢水罐到五角星的模具模具里就可以很容易地制做五角星. 我们也可以将类比喻成做五角星的模具.  而生产出的一个个五角 ...

  5. OutputFormat接口实现类

    OutputFormat接口实现类 OutputFormat接口实现类概念 自定义OutputFormat介绍 1. 使用场景 2. 自定义OutputFormat步骤 自定义OutputFormat ...

  6. 总结Servlet体系接口和类

    --核心-- javax.servlet.Servlet接口 构造方法服务对象:主线程 init方法.destory方法的服务对象:主线程 service方法的服务对象是:分支线程 --重要-- ja ...

  7. JDBC—01—JDBC简介;JDBC常用接口与类;

    一. JDBC 简介 1 什么是 JDBC JDBC(Java DataBase Connectivity)java 数据库连接 是 JavaEE 平台下的技术规范 定义了在 Java 语言中连接数据 ...

  8. 实验八 接口与实现接口的类

    实验八 接口与实现接口的类 一.程序代码 public class yuanzhui extends Rectangle implements Area,Volume { private double ...

  9. 把接口作为函数的参数,那么任何实现了接口的类的实例都可以作为此函数的参数传递...

    把接口作为函数的参数,那么任何实现了接口的类的实例都可以作为此函数的参数传递 转载于:https://www.cnblogs.com/xiaodangshan/p/9784315.html

  10. Java教程之JDBC中的常用接口和类

    JDBC定义了一系列操作数据库的接口和类,这些接口和类位于java.sql包中.接下来,本节将详细介绍JDBC的常用API. Driver接口 Driver接口是所有JDBC驱动程序必须要实现的接口, ...

最新文章

  1. c++string类默认函数实现
  2. android arrays.xml 二维数组,android中怎的从xml文件中解析一个二维数组
  3. RoR开发环境搭建 RAILS install log
  4. 【前端基础进阶】JS-Object 功能详解
  5. 2021,我的输入输出
  6. Spring RESTful Web服务中的异常处理
  7. python方向键键值_python字典键值对的添加和遍历方法
  8. 为啥计算机课要带u盘,电脑课上,student 关掉以后会不会被老师发现,同时电脑机上,老师不知干了什么,U盘没法使用,求解...
  9. linux 每日学一点《linux性能测试初步概况》
  10. HTTP常用参数对照表
  11. Stm32串口通信基础实验
  12. 如何判断一个PCIe的capability是哪个capability
  13. Mac 清理存储空间
  14. 如何随意复制网页上的文字
  15. linux strcpy函数,C语言中函数strcpy ,strncpy ,strlcpy,strcpy_s的用法
  16. 这些年我是如何在知乎安稳引流不被封号的
  17. Java SE day21_网络编程
  18. 毕业季,回忆大学生活
  19. 【FTK Imager篇】FTK Imager中文设置教程
  20. mysql连接报10061

热门文章

  1. java如何导出excel_JAVA如何导出EXCEL表格
  2. maven的全局setting及用户setting
  3. 联通发布沃Phone,全球为之震动
  4. 以正方体一个顶点进行旋转的3D立方体动画
  5. CHM格式打开以后无法显示解决
  6. 使用 Cloudflare 进行域名跳转(重定向)
  7. Word文档进入只读模式
  8. c语言strlen转义字符,转义字符 sizeof strlen
  9. Java //PP1.3 编写一个程序,分行显示你的名字、生日、爱好、最喜欢的书及最喜欢的电影。
  10. debian 7 网络安装后无法联接wifi