草庐IT

day22--Java集合05

liyuelian 2023-04-16 原文

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 - 等价于 Java 中的 Ruby Hash - 2

    我真的很习惯使用Ruby编写以下代码:my_hash={}my_hash['test']=1Java中对应的数据结构是什么? 最佳答案 HashMapmap=newHashMap();map.put("test",1);我假设? 关于java-等价于Java中的RubyHash,我们在StackOverflow上找到一个类似的问题: https://stackoverflow.com/questions/22737685/

  2. java - 从 JRuby 调用 Java 类的问题 - 2

    我正在尝试使用boilerpipe来自JRuby。我看过guide从JRuby调用Java,并成功地将它与另一个Java包一起使用,但无法弄清楚为什么同样的东西不能用于boilerpipe。我正在尝试基本上从JRuby中执行与此Java等效的操作:URLurl=newURL("http://www.example.com/some-location/index.html");Stringtext=ArticleExtractor.INSTANCE.getText(url);在JRuby中试过这个:require'java'url=java.net.URL.new("http://www

  3. java - 我的模型类或其他类中应该有逻辑吗 - 2

    我只想对我一直在思考的这个问题有其他意见,例如我有classuser_controller和classuserclassUserattr_accessor:name,:usernameendclassUserController//dosomethingaboutanythingaboutusersend问题是我的User类中是否应该有逻辑user=User.newuser.do_something(user1)oritshouldbeuser_controller=UserController.newuser_controller.do_something(user1,user2)我

  4. java - 什么相当于 ruby​​ 的 rack 或 python 的 Java wsgi? - 2

    什么是ruby​​的rack或python的Java的wsgi?还有一个路由库。 最佳答案 来自Python标准PEP333:Bycontrast,althoughJavahasjustasmanywebapplicationframeworksavailable,Java's"servlet"APImakesitpossibleforapplicationswrittenwithanyJavawebapplicationframeworktoruninanywebserverthatsupportstheservletAPI.ht

  5. Observability:从零开始创建 Java 微服务并监控它 (二) - 2

    这篇文章是继上一篇文章“Observability:从零开始创建Java微服务并监控它(一)”的续篇。在上一篇文章中,我们讲述了如何创建一个Javaweb应用,并使用Filebeat来收集应用所生成的日志。在今天的文章中,我来详述如何收集应用的指标,使用APM来监控应用并监督web服务的在线情况。源码可以在地址 https://github.com/liu-xiao-guo/java_observability 进行下载。摄入指标指标被视为可以随时更改的时间点值。当前请求的数量可以改变任何毫秒。你可能有1000个请求的峰值,然后一切都回到一个请求。这也意味着这些指标可能不准确,你还想提取最小/

  6. 【Java 面试合集】HashMap中为什么引入红黑树,而不是AVL树呢 - 2

    HashMap中为什么引入红黑树,而不是AVL树呢1.概述开始学习这个知识点之前我们需要知道,在JDK1.8以及之前,针对HashMap有什么不同。JDK1.7的时候,HashMap的底层实现是数组+链表JDK1.8的时候,HashMap的底层实现是数组+链表+红黑树我们要思考一个问题,为什么要从链表转为红黑树呢。首先先让我们了解下链表有什么不好???2.链表上述的截图其实就是链表的结构,我们来看下链表的增删改查的时间复杂度增:因为链表不是线性结构,所以每次添加的时候,只需要移动一个节点,所以可以理解为复杂度是N(1)删:算法时间复杂度跟增保持一致查:既然是非线性结构,所以查询某一个节点的时候

  7. postman——集合——执行集合——测试脚本——pm对象简单示例02 - 2

    //1.验证返回状态码是否是200pm.test("Statuscodeis200",function(){pm.response.to.have.status(200);});//2.验证返回body内是否含有某个值pm.test("Bodymatchesstring",function(){pm.expect(pm.response.text()).to.include("string_you_want_to_search");});//3.验证某个返回值是否是100pm.test("Yourtestname",function(){varjsonData=pm.response.json

  8. 【Java入门】使用Java实现文件夹的遍历 - 2

    遍历文件夹我们通常是使用递归进行操作,这种方式比较简单,也比较容易理解。本文为大家介绍另一种不使用递归的方式,由于没有使用递归,只用到了循环和集合,所以效率更高一些!一、使用递归遍历文件夹整体思路1、使用File封装初始目录,2、打印这个目录3、获取这个目录下所有的子文件和子目录的数组。4、遍历这个数组,取出每个File对象4-1、如果File是否是一个文件,打印4-2、否则就是一个目录,递归调用代码实现publicclassSearchFile{publicstaticvoidmain(String[]args){//初始目录Filedir=newFile("d:/Dev");Datebeg

  9. java - 为什么 ruby​​ modulo 与 java/other lang 不同? - 2

    我基本上来自Java背景并且努力理解Ruby中的模运算。(5%3)(-5%3)(5%-3)(-5%-3)Java中的上述操作产生,2个-22个-2但在Ruby中,相同的表达式会产生21个-1-2.Ruby在逻辑上有多擅长这个?模块操作在Ruby中是如何实现的?如果将同一个操作定义为一个web服务,两个服务如何匹配逻辑。 最佳答案 在Java中,模运算的结果与被除数的符号相同。在Ruby中,它与除数的符号相同。remainder()在Ruby中与被除数的符号相同。您可能还想引用modulooperation.

  10. java - Ruby 相当于 Java 的 Collections.unmodifiableList 和 Collections.unmodifiableMap - 2

    Java的Collections.unmodifiableList和Collections.unmodifiableMap在Ruby标准API中是否有等价物? 最佳答案 使用freeze应用程序接口(interface):Preventsfurthermodificationstoobj.ARuntimeErrorwillberaisedifmodificationisattempted.Thereisnowaytounfreezeafrozenobject.SeealsoObject#frozen?.Thismethodretur

随机推荐