day22--Java集合05

打印 上一主题 下一主题

主题 843|帖子 843|积分 2529

Java集合05

11.HashSet课堂练习

11.1课堂练习1

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

  • 创建3个Employee对象放入HashSet中
  • 当name和age的值相同时,认为是相同员工,不能添加到HashSet集合中
思路:不同对象的哈希值一般会不一样,导致在添加对象时可能会在table数组的不同位置添加,因此想要比较对象的属性值,就要重写hashCode方法,使具有相同属性的对象具有一样的hash值,这样才能在插入时比较对象的值;但不同的对象也可能具有相同的hash值,所以要重写equals方法来比较对象属性值
如下图:在add()方法最终调用的putVal()方法中可以看出,如果插入的新元素的哈希值相同 且 值也相同 就不加入

例子:
  1. package li.collections.set.hashset;
  2. import java.util.HashSet;
  3. import java.util.Objects;
  4. @SuppressWarnings("all")
  5. public class HashSetPractice {
  6.     public static void main(String[] args) {
  7.         HashSet hashSet = new HashSet();
  8.         hashSet.add(new Employee("jack", 18));
  9.         hashSet.add(new Employee("smith", 18));
  10.         hashSet.add(new Employee("jack", 18));
  11.         System.out.println(hashSet);//[Employee{name='smith', age=18.0}, Employee{name='jack', age=18.0}]
  12.     }
  13. }
  14. class Employee {
  15.     private String name;
  16.     private double age;
  17.     public Employee(String name, double age) {
  18.         this.name = name;
  19.         this.age = age;
  20.     }
  21.     public String getName() {
  22.         return name;
  23.     }
  24.     public void setName(String name) {
  25.         this.name = name;
  26.     }
  27.     public double getAge() {
  28.         return age;
  29.     }
  30.     public void setAge(double age) {
  31.         this.age = age;
  32.     }
  33.     @Override
  34.     public String toString() {
  35.         return "Employee{" +
  36.                 "name='" + name + '\'' +
  37.                 ", age=" + age +
  38.                 '}';
  39.     }
  40.     @Override
  41.     public boolean equals(Object o) {
  42.         if (this == o) return true;
  43.         if (o == null || getClass() != o.getClass()) return false;
  44.         Employee employee = (Employee) o;
  45.         return Double.compare(employee.age, age) == 0 && Objects.equals(name, employee.name);
  46.     }
  47.     @Override
  48.     public int hashCode() {
  49.         return Objects.hash(name, age);
  50.     }
  51. }
复制代码

快捷键: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
要求:

  • 创建3个Employee对象放入到HashSet中
  • 当name和birthday的值相同时,热卫视相同员工,不能添加到HashSet集合中
思路:和练习1思路一致,不同的是MyDate类也要重写equals()和hashCode()方法
练习:
  1. package li.collections.set.hashset;
  2. import java.util.HashSet;
  3. import java.util.Objects;
  4. @SuppressWarnings("all")
  5. public class HashSetPractice2 {
  6.     public static void main(String[] args) {
  7.         HashSet hashSet = new HashSet();
  8.         hashSet.add(new Employee("jack",8000,new MyDate(1997,12,23)));
  9.         hashSet.add(new Employee("jack",8000,new MyDate(1997,12,23)));
  10.         hashSet.add(new Employee("jack",8000,new MyDate(1997,12,23)));
  11.         hashSet.add(new Employee("jack",8000,new MyDate(1997,12,23)));
  12.         System.out.println(hashSet);//[Employee{name='jack', sal=8000.0, birthday=MyDate{year=1997, month=12, day=23}}]
  13.     }
  14. }
  15. class Employee {
  16.     private String name;
  17.     private double sal;
  18.     private MyDate birthday;
  19.     public Employee(String name, double sal, MyDate birthday) {
  20.         this.name = name;
  21.         this.sal = sal;
  22.         this.birthday = birthday;
  23.     }
  24.     public String getName() {
  25.         return name;
  26.     }
  27.     public void setName(String name) {
  28.         this.name = name;
  29.     }
  30.     public double getSal() {
  31.         return sal;
  32.     }
  33.     public void setSal(double sal) {
  34.         this.sal = sal;
  35.     }
  36.     public MyDate getBirthday() {
  37.         return birthday;
  38.     }
  39.     public void setBirthday(MyDate birthday) {
  40.         this.birthday = birthday;
  41.     }
  42.     @Override
  43.     public boolean equals(Object o) {
  44.         if (this == o) return true;
  45.         if (o == null || getClass() != o.getClass()) return false;
  46.         Employee employee = (Employee) o;
  47.         return Double.compare(employee.sal, sal) == 0 && Objects.equals(name, employee.name) && Objects.equals(birthday, employee.birthday);
  48.     }
  49.     @Override
  50.     public int hashCode() {
  51.         return Objects.hash(name, sal, birthday);
  52.     }
  53.     @Override
  54.     public String toString() {
  55.         return "Employee{" +
  56.                 "name='" + name + '\'' +
  57.                 ", sal=" + sal +
  58.                 ", birthday=" + birthday +
  59.                 '}';
  60.     }
  61. }
  62. class MyDate {
  63.     private int year;
  64.     private int month;
  65.     private int day;
  66.     public MyDate(int year, int month, int day) {
  67.         this.year = year;
  68.         this.month = month;
  69.         this.day = day;
  70.     }
  71.     public int getYear() {
  72.         return year;
  73.     }
  74.     public void setYear(int year) {
  75.         this.year = year;
  76.     }
  77.     public int getMonth() {
  78.         return month;
  79.     }
  80.     public void setMonth(int month) {
  81.         this.month = month;
  82.     }
  83.     public int getDay() {
  84.         return day;
  85.     }
  86.     public void setDay(int day) {
  87.         this.day = day;
  88.     }
  89.     @Override
  90.     public boolean equals(Object o) {
  91.         if (this == o) return true;
  92.         if (o == null || getClass() != o.getClass()) return false;
  93.         MyDate myDate = (MyDate) o;
  94.         return year == myDate.year && month == myDate.month && day == myDate.day;
  95.     }
  96.     @Override
  97.     public int hashCode() {
  98.         return Objects.hash(year, month, day);
  99.     }
  100.     @Override
  101.     public String toString() {
  102.         return "MyDate{" +
  103.                 "year=" + year +
  104.                 ", month=" + month +
  105.                 ", day=" + day +
  106.                 '}';
  107.     }
  108. }
复制代码

12.LinkedHashSet

12.1LinkedHashSet底层


  • LinkedHashSet是HashSet的子类
  • LinkedHashSet底层是一个LinkedHashMap(LinkedHashMap是HashMap的子类),底层维护了一个       数组+双向链表
  • LinkedHashSet根据元素的hashCode值来决定元素的存储位置,同时使用链表维护元素的次序,这是元素看起来是以插入顺序保存的
  • LinkedHashSet不允许重复元素
说明:
1)在LinkedHashSet中维护了一个hash表和双向链表(LinkedHashSet中有head和tail)
2)每一个节点都有前后指针(before和after属性),形成双向链表
3)在添加一个元素时,先求hash值,再求索引。确定该元素在table的位置然后将添加的元素加入到双向链表(如果已经村存在,就不添加,原理和HashSet一样)
  1. tail.next = newElement;//将新添加的节点连接至尾节点的后面
  2. newElement.pre = tail;//将尾节点设为新结点的前驱结点
  3. tail = newElement;//将新节点设为尾节点
复制代码
4)这样,遍历LinkedHashSet也能确保插入顺序和遍历顺序一致
例子1:LinkedHashSet底层分析
  1. package li.collections.set.hashset;
  2. import java.util.LinkedHashSet;
  3. import java.util.Set;
  4. @SuppressWarnings("all")
  5. public class LinkedHashSetSource {
  6.     public static void main(String[] args) {
  7.         Set set = new LinkedHashSet();
  8.         set.add(new String("AA"));
  9.         set.add(456);
  10.         set.add(456);
  11.         set.add(new Customer("刘", 1001));
  12.         set.add(123);
  13.         set.add("jack");
  14.         System.out.println(set);
  15.     }
  16. }
  17. class Customer {
  18.     private String name;
  19.     private int number;
  20.     public Customer(String name, int number) {
  21.         this.name = name;
  22.         this.number = number;
  23.     }
  24.     @Override
  25.     public String toString() {
  26.         return "Customer{" +
  27.                 "name='" + name + '\'' +
  28.                 ", number=" + number +
  29.                 '}';
  30.     }
  31. }
复制代码
如下图,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一样,就认为是相同元素,不能添加
练习:
  1. package li.collections.set.hashset;
  2. import java.util.LinkedHashSet;
  3. import java.util.Objects;
  4. @SuppressWarnings("all")
  5. public class LinkedHashSetPractice {
  6.     public static void main(String[] args) {
  7.         LinkedHashSet linkedHashSet = new LinkedHashSet();
  8.         linkedHashSet.add(new Car("奥拓",100_000));
  9.         linkedHashSet.add(new Car("保时捷",990_000));
  10.         linkedHashSet.add(new Car("法拉利",3100_000));
  11.         linkedHashSet.add(new Car("特斯拉",100_000));
  12.         linkedHashSet.add(new Car("特斯拉",100_000));
  13.         for (Object o:linkedHashSet) {
  14.             System.out.println(o);
  15.         }
  16.     }
  17. }
  18. class Car{
  19.     private String name;
  20.     private double price;
  21.     public Car(String name, double price) {
  22.         this.name = name;
  23.         this.price = price;
  24.     }
  25.     @Override
  26.     public String toString() {
  27.         return "Car{" +
  28.                 "name='" + name + '\'' +
  29.                 ", price=" + price +
  30.                 '}';
  31.     }
  32.     @Override
  33.     public boolean equals(Object o) {
  34.         if (this == o) return true;
  35.         if (o == null || getClass() != o.getClass()) return false;
  36.         Car car = (Car) o;
  37.         return Double.compare(car.price, price) == 0 && Objects.equals(name, car.name);
  38.     }
  39.     @Override
  40.     public int hashCode() {
  41.         return Objects.hash(name, price);
  42.     }
  43.    
  44. }
复制代码
在没有重写hashCode()和equals()之前不同的对象实例的hash值一般是不一样的,因此可以插入name和price相同的对象数据。重写之后可以看到相同属性的对象无法插入了。具体解题思路和11HashSet的课堂练习一致。
13.Map接口


13.1Map接口特点

Map接口实现类的特点:( JDK8的Map接口特点 )

  • Map和Collection并列存在。Map用于保存具有映射关系的数据:key-value(双列元素)
  • Map中的key和value可以是任何引用类的数据,会封装到HashMap$Node对象中
  • Map中的key不允许重复(key值不允许重复,重复的话就会用新的值替换/覆盖旧的值e = p--->详细原因和HashSet一样,详见10.1-10.3)
  • Map中的value允许重复(hash值取决于key)
  • Map中的key可以为null,value也可以为null(注意key的null最多只能有一个,value的null可以有多个)
  • 常用String类作为Map的key
  • key和value之间存在单向一对一关系,即通过指定的key总能找到对应的value
例子:map的简单使用
  1. package li.map;
  2. import java.util.HashMap;
  3. import java.util.Map;
  4. public class MapIntroduce {
  5.     @SuppressWarnings("all")
  6.     public static void main(String[] args) {
  7.         Map map = new HashMap();
  8.         map.put("no1","北京");//k-v
  9.         map.put("no4","深圳");//k-v
  10.         map.put("no4","长沙");//no4=长沙-->key值不允许重复,重复的话就会用新的值替换/覆盖旧的值 e=p
  11.         map.put("no5","北京");//value可以重复,hash值取决于key
  12.         map.put(null,null);
  13.         map.put(null,"abc");//key不能重复,因此这里的值会将上一行的值覆盖 null=abc
  14.         map.put("abc",null);//value可以重复
  15.         map.put(new Object(),123);
  16.         //无序的:原因是底层是按计算的hash值来存放
  17.         //{null=abc, no1=北京, no4=长沙, abc=null, no5=北京, java.lang.Object@1b6d3586=123}
  18.         System.out.println(map);
  19.         //通过指定的key总能找到对应的value
  20.         System.out.println(map.get(null));//abc
  21.         System.out.println(map.get("no1"));
  22.     }
  23. }
复制代码
免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!

本帖子中包含更多资源

您需要 登录 才可以下载或查看,没有账号?立即注册

x
回复

使用道具 举报

0 个回复

倒序浏览

快速回复

您需要登录后才可以回帖 登录 or 立即注册

本版积分规则

万万哇

金牌会员
这个人很懒什么都没写!
快速回复 返回顶部 返回列表