Java集合05

11.HashSet课堂练习

11.1课堂练习1

定义一个Employee类,该类包括:private成员属性name,age

要求:

  1. 创建3个Employee对象放入HashSet中
  2. 当name和age的值相同时,认为是相同员工,不能添加到HashSet集合中

思路:不同对象的哈希值一般会不一样,导致在添加对象时可能会在table数组的不同位置添加,因此想要比较对象的属性值,就要重写hashCode方法,使具有相同属性的对象具有一样的hash值,这样才能在插入时比较对象的值;但不同的对象也可能具有相同的hash值,所以要重写equals方法来比较对象属性值

如下图:在add()方法最终调用的putVal()方法中可以看出,如果插入的新元素的哈希值相同 且 值也相同 就不加入

例子:

package li.collections.set.hashset;

import java.util.HashSet;
import java.util.Objects; @SuppressWarnings("all")
public class HashSetPractice {
public static void main(String[] args) {
HashSet hashSet = new HashSet();
hashSet.add(new Employee("jack", 18));
hashSet.add(new Employee("smith", 18));
hashSet.add(new Employee("jack", 18)); System.out.println(hashSet);//[Employee{name='smith', age=18.0}, Employee{name='jack', age=18.0}] }
} class Employee {
private String name;
private double age; public Employee(String name, double age) {
this.name = name;
this.age = age;
} public String getName() {
return name;
} public void setName(String name) {
this.name = name;
} public double getAge() {
return age;
} public void setAge(double age) {
this.age = age;
} @Override
public String toString() {
return "Employee{" +
"name='" + name + '\'' +
", age=" + age +
'}';
} @Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
Employee employee = (Employee) o;
return Double.compare(employee.age, age) == 0 && Objects.equals(name, employee.name);
} @Override
public int hashCode() {
return Objects.hash(name, age);
}
}

快捷键:alt+insert选择equals()and hashCode()快速重写

如果name和age的值相同,在使用equals时,返回true

如果name和age的值相同,在计算hashCode的时候返回相同的hash值

11.2课堂练习2

定义一个Employee类,该类包含:private成员属性name,sal,birthday,其中birthday为MyDate类型,属性包括year,month,day

要求:

  1. 创建3个Employee对象放入到HashSet中
  2. 当name和birthday的值相同时,认为是相同员工,不能添加到HashSet集合中

思路:和练习1思路一致,不同的是MyDate类也要重写equals()和hashCode()方法

练习:

package li.collections.set.hashset;

import java.util.HashSet;
import java.util.Objects; @SuppressWarnings("all")
public class HashSetPractice2 {
public static void main(String[] args) {
HashSet hashSet = new HashSet();
hashSet.add(new Employee("jack",8000,new MyDate(1997,12,23)));
hashSet.add(new Employee("jack",8000,new MyDate(1997,12,23)));
hashSet.add(new Employee("jack",8000,new MyDate(1997,12,23)));
hashSet.add(new Employee("jack",8000,new MyDate(1997,12,23))); System.out.println(hashSet);//[Employee{name='jack', sal=8000.0, birthday=MyDate{year=1997, month=12, day=23}}] }
} class Employee {
private String name;
private double sal;
private MyDate birthday; public Employee(String name, double 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 double getSal() {
return sal;
} public void setSal(double sal) {
this.sal = sal;
} public MyDate getBirthday() {
return birthday;
} public void setBirthday(MyDate birthday) {
this.birthday = birthday;
} @Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
Employee employee = (Employee) o;
return Double.compare(employee.sal, sal) == 0 && Objects.equals(name, employee.name) && Objects.equals(birthday, employee.birthday);
} @Override
public int hashCode() {
return Objects.hash(name, sal, birthday);
} @Override
public String toString() {
return "Employee{" +
"name='" + name + '\'' +
", sal=" + sal +
", birthday=" + 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 +
'}';
}
}

12.LinkedHashSet

12.1LinkedHashSet底层

  1. LinkedHashSet是HashSet的子类
  2. LinkedHashSet底层是一个LinkedHashMap(LinkedHashMap是HashMap的子类),底层维护了一个 数组+双向链表
  3. LinkedHashSet根据元素的hashCode值来决定元素的存储位置,同时使用链表维护元素的次序,这使元素看起来是以插入顺序保存的
  4. LinkedHashSet不允许重复元素

说明:

1)在LinkedHashSet中维护了一个hash表和双向链表(LinkedHashSet中有head和tail)

2)每一个节点都有前后指针(before和after属性),形成双向链表

3)在添加一个元素时,先求hash值,再求索引。确定该元素在table的位置然后将添加的元素加入到双向链表(如果已经存在,就不添加,原理和HashSet一样)

tail.next = newElement;//将新添加的节点连接至尾节点的后面
newElement.pre = tail;//将尾节点设为新结点的前驱结点
tail = newElement;//将新节点设为尾节点

4)这样,遍历LinkedHashSet也能确保插入顺序和遍历顺序一致

例子1:LinkedHashSet底层分析

package li.collections.set.hashset;

import java.util.LinkedHashSet;
import java.util.Set; @SuppressWarnings("all") public class LinkedHashSetSource {
public static void main(String[] args) {
Set set = new LinkedHashSet();
set.add(new String("AA"));
set.add(456);
set.add(456);
set.add(new Customer("刘", 1001));
set.add(123);
set.add("jack");
System.out.println(set);
}
} class Customer {
private String name;
private int number; public Customer(String name, int number) {
this.name = name;
this.number = number;
} @Override
public String toString() {
return "Customer{" +
"name='" + name + '\'' +
", number=" + number +
'}';
}
}

如下图,LinkedHashSet不允许重复值,且插入顺序和取出顺序一致

Set set = new LinkedHashSet();出打上断点调试:

如下图:可以看到,LinkedHashSet底层是一个LinkedHashMap

如下图所示:点击map展开,Step Over之后可以看到第一次添加数据时,直接将数组table扩容到16(数组下标从零开始),存放的节点类型是LinkedHashMap$Entry

数组是HashMap$Node[ ]

存放的元素/数据是LinkedHashMap$Entry类型(LinkedHashMap的内部类Entry继承了HashMap的内部类Node)



如下图:继续添加数据,可以看到在索引为8 的位置添加了新的数据456,点开索引为0 的节点,可以看到这时原来节点的after指向了新的节点,并且新结点的before指向了原来的节点,形成了双向链表


如下图所示:在add()底层仍然是和HashSet调用了相同的方法,详情见10.3HashSet源码详解



以此类推,形成一条双向链表:

12.1.LinkedHashSet练习

有一个Car类,存在两个私有属性name和price

要求:如果两个Car对象的name和price一样,就认为是相同元素,不能添加

练习:

package li.collections.set.hashset;

import java.util.LinkedHashSet;
import java.util.Objects; @SuppressWarnings("all")
public class LinkedHashSetPractice {
public static void main(String[] args) {
LinkedHashSet linkedHashSet = new LinkedHashSet();
linkedHashSet.add(new Car("奥拓",100_000));
linkedHashSet.add(new Car("保时捷",990_000));
linkedHashSet.add(new Car("法拉利",3100_000));
linkedHashSet.add(new Car("特斯拉",100_000));
linkedHashSet.add(new Car("特斯拉",100_000));
for (Object o:linkedHashSet) {
System.out.println(o);
}
}
} class Car{
private String name;
private double price; public Car(String name, double price) {
this.name = name;
this.price = price;
} @Override
public String toString() {
return "Car{" +
"name='" + name + '\'' +
", price=" + price +
'}';
} @Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
Car car = (Car) o;
return Double.compare(car.price, price) == 0 && Objects.equals(name, car.name);
}
@Override
public int hashCode() {
return Objects.hash(name, price);
} }

在没有重写hashCode()和equals()之前不同的对象实例的hash值一般是不一样的,因此可以插入name和price相同的对象数据。重写之后可以看到相同属性的对象无法插入了。具体解题思路和11HashSet的课堂练习一致。

day22--Java集合05的更多相关文章

  1. Java 集合系列05之 LinkedList详细介绍(源码解析)和使用示例

    概要  前面,我们已经学习了ArrayList,并了解了fail-fast机制.这一章我们接着学习List的实现类——LinkedList.和学习ArrayList一样,接下来呢,我们先对Linked ...

  2. Java 集合系列 05 Vector详细介绍(源码解析)和使用示例

    java 集合系列目录: Java 集合系列 01 总体框架 Java 集合系列 02 Collection架构 Java 集合系列 03 ArrayList详细介绍(源码解析)和使用示例 Java ...

  3. Java 集合系列04之 fail-fast总结(通过ArrayList来说明fail-fast的原理、解决办法)

    概要 前面,我们已经学习了ArrayList.接下来,我们以ArrayList为例,对Iterator的fail-fast机制进行了解.内容包括::1 fail-fast简介2 fail-fast示例 ...

  4. Java 集合系列目录(Category)

    下面是最近总结的Java集合(JDK1.6.0_45)相关文章的目录. 01. Java 集合系列01之 总体框架 02. Java 集合系列02之 Collection架构 03. Java 集合系 ...

  5. Java 集合系列11之 Hashtable详细介绍(源码解析)和使用示例

    概要 前一章,我们学习了HashMap.这一章,我们对Hashtable进行学习.我们先对Hashtable有个整体认识,然后再学习它的源码,最后再通过实例来学会使用Hashtable.第1部分 Ha ...

  6. Java集合系列:-----------04fail-fast总结(通过ArrayList来说明fail-fast的原理以及解决办法)

    前面,我们已经学习了ArrayList.接下来,我们以ArrayList为例,对Iterator的fail-fast机制进行了解.内容包括::1 fail-fast简介2 fail-fast示例3 f ...

  7. Java集合源码学习(三)LinkedList分析

    前面学习了ArrayList的源码,数组是顺序存储结构,存储区间是连续的,占用内存严重,故空间复杂度很大.但数组的二分查找时间复杂度小,为O(1),数组的特点是寻址容易,插入和删除困难.今天学习另外的 ...

  8. Java 集合系列08之 List总结(LinkedList, ArrayList等使用场景和性能分析)

    概要 前面,我们学完了List的全部内容(ArrayList, LinkedList, Vector, Stack). Java 集合系列03之 ArrayList详细介绍(源码解析)和使用示例 Ja ...

  9. Java 集合系列14之 Map总结(HashMap, Hashtable, TreeMap, WeakHashMap等使用场景)

    概要 学完了Map的全部内容,我们再回头开开Map的框架图. 本章内容包括:第1部分 Map概括第2部分 HashMap和Hashtable异同第3部分 HashMap和WeakHashMap异同 转 ...

  10. Java集合的Stack、Queue、Map的遍历

    Java集合的Stack.Queue.Map的遍历   在集合操作中,常常离不开对集合的遍历,对集合遍历一般来说一个foreach就搞定了,但是,对于Stack.Queue.Map类型的遍历,还是有一 ...

随机推荐

  1. AIX6.1修改时区/修改时间

    环境 AIX6.1 修改时间 格式:date -n mmddHHMMYY  #mm表示月分,dd表示日期,HH表示小时,MM表示分钟,YY表示年份. 例子:date -n 1204171622  #修 ...

  2. Docker从认识到实践再到底层原理(二-2)|Namespace+cgroups

    前言 那么这里博主先安利一些干货满满的专栏了! 首先是博主的高质量博客的汇总,这个专栏里面的博客,都是博主最最用心写的一部分,干货满满,希望对大家有帮助. 高质量博客汇总 然后就是博主最近最花时间的一 ...

  3. 19c RAC 告警日志报错 ORA 7445 [pevm_icd_call_common()+225]

    问题现象: 在一套2节点的19c RAC 环境下,节点2 alert告警 ORA 7445,且频度固定为每分钟报一次:期间有重启实例,但故障依旧: ========================== ...

  4. TfrxReport.Clear。尽量慎用。

    for MyXuHaoKey in MyXuHaoJianRongSanJieKouDataDicApi.KeySortList do begin //标记下打印编号,吸入淘打的客户 MyTradeA ...

  5. Java发送mail和C#发送mail

    Java发送mail 阿里云邮箱,配置公司邮箱服务器,邮箱地址,授权码(运维同事提供,听说阿里云邮箱的授权码和密码一样),端口465,测试能发送. /** * 发送简单的文本邮件 */ public ...

  6. 移位寄存器的设计(VHDL)及testbench的编写

    移位寄存器是一种常用的存储元件,此处由D触发器构成,如下图所示. 当时钟边沿到来时,存储在移位寄存器的数据朝一个方向移动一个BIT位. 移位寄存器的功能主要为:串并转换,并串转换和同步延迟. vhdl ...

  7. 浅谈一下对于 js 中的 this 的理解

    浅谈一下对于 js 中的 this 的理解 对于 this 值的定义: 简单来说 this 是一个对象,这个对象具体的值是什么,取决于运行时的环境,即代码执行时的环境. MDN: 当前执行上下文( g ...

  8. C++——编译和链接原理笔记

    我们在学习和开发C++程序中,理解编译和链接的原理至关重要.下面将学习一下C++程序是如何从源代码转换为可执行文件的过程,并结合示例代码进行说明.也是为了解开自己在刚学习C++的时候,编译时间长的疑惑 ...

  9. mysql备份导入

    mysqldump -uroot -p --single-transaction --master-data=2 --flush-logs --flush-privileges --routines= ...

  10. NC16610 [NOIP2009]Hankson的趣味题

    题目链接 题目 题目描述 Hanks博士是BT(Bio-Tech,生物技术)领域的知名专家,他的儿子名叫Hankson.现在,刚刚放学回家的Hankson正在思考一个有趣的问题. 今天在课堂上,老师讲 ...