day14_JAVAOOP
课程目标
1. 【理解】Set集合的特点 2. 【理解】Set集合不重复的原理 3. 【掌握】HaseSet集合的基本使用 4. 【理解】LinkedHashSet的特点 5. 【理解】Map集合的特点 6. 【掌握】HashMap的使用 7. 【理解】LinkedHashMap的特点 8. 【掌握】Map集合的案例 9. 【掌握】模拟斗地主案例
Set集合
Set集合概述
java.util.Set
接口和java.util.List
接口一样,同样继承自Collection
接口,它与Collection
接口中的方法基本一致,并没有对Collection
接口进行功能上的扩充,只是比Collection
接口更加严格了。与List
接口不同的是,Set
接口中元素无序,并且都会以某种规则保证存入的元素不出现重复。
Set
集合有多个子类,这里我们介绍其中的java.util.HashSet
、java.util.LinkedHashSet
这两个集合。
Set集合的特点
- Set集合中的元素不可重复
- Set集合没有索引
总结: 无序,唯一
HashSet集合
什么是HashSet集合
java.util.HashSet
是Set
接口的一个实现类,它所存储的元素是不可重复的,并且元素都是无序的(即存取顺序不一致)。java.util.HashSet
底层的实现其实是一个java.util.HashMap
支持,由于我们暂时还未学习,先做了解。
HashSet
是根据对象的哈希值来确定元素在集合中的存储位置,因此具有良好的存取和查找性能。保证元素唯一性的方式依赖于:hashCode
与equals
方法。
HashSet集合的特点
- HashSet集合中的元素不可重复
- HashSet集合没有索引
- HashSet集合是无序的(存储元素的顺序与取出元素顺序可能不一致)
总结:无序,唯一
HashSet代码演示
/** Collection |--List 有序(存储顺序和取出顺序一致),可重复 |--Set 无序(存储顺序和取出顺序不一致),唯一 HashSet:它不保证 set 的迭代顺序;特别是它不保证该顺序恒久不变。 注意: 虽然Set集合的元素无序,但是,作为集合来说,它肯定有它自己的存储顺序, 而你的顺序恰好和它的存储顺序一致,这代表不了有序,你可以多存储一些数据,就能看到效果。 */ public class HashSetDemo { public static void main(String[] args) { //创建 Set集合 HashSet<String> set = new HashSet<String>(); //添加元素 set.add(new String("cba")); set.add("abc"); set.add("bac"); set.add("cba"); //遍历 for (String name : set) { System.out.println(name); } } }
如何保证Hashset集合唯一? 底层依赖 两个方法:hashCode()和equals()。 步骤: 首先比较哈希值 如果相同,继续走,比较地址值或者走equals() 如果不同,就直接添加到集合中 按照方法的步骤来说: 先看hashCode()值是否相同 相同:继续走equals()方法 返回true: 说明元素重复,就不添加 返回false:说明元素不重复,就添加到集合 不同:就直接把元素添加到集合 如果类没有重写这两个方法,默认使用的Object()。一般来说一样。 而String类重写了hashCode()和equals()方法,所以,它就可以把内容相同的字符串去掉。只留下一个。
HashSet存储自定义类型元素
- 定义Student类
public class Student { private String name; private int age; public Student() { } public Student(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; } @Override public String toString() { return "Student{" + "name='" + name + '\'' + ", age=" + age + '}'; } //不需要你手动重写Object hashCode和equals ,再去测试 @Override public boolean equals(Object o) { if (this == o) return true; if (o == null || getClass() != o.getClass()) return false; Student student = (Student) o; return age == student.age && Objects.equals(name, student.name); } @Override public int hashCode() { return Objects.hash(name, age); } }
- 定义测试类
public class HashSetDemo2 { public static void main(String[] args) { //创建集合对象 该集合中存储 Student类型对象 HashSet<Student> stuSet = new HashSet<Student>(); //存储 stuSet.add(new Student("于谦", 43)); stuSet.add(new Student("于谦", 43)); stuSet.add(new Student("郭麒麟", 23)); stuSet.add(new Student("郭麒麟", 23)); for (Student stu2 : stuSet) { System.out.println(stu2); } } }
- 结果分析
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-RTN5ZlPY-1672475567262)(assets/image-20201119201258204.png)]
我们发现有重复的元素,不是Set集合中的元素是不能重复的吗,为什么存储了重复的元素的呢? 下面我们就来分析分析Set集合存储不重复的原理!!!
/* * 需求:存储自定义对象,并保证元素的唯一性 * 要求:如果两个对象的成员变量值都相同,则为同一个元素。 * * 目前是不符合我的要求的:因为我们知道HashSet底层依赖的是hashCode()和equals()方法。 * 而这hashCode()和equals()两个方法我们在学生类中没有重写,所以,默认使用的是Object类。 * 这个时候,他们的哈希值是不会一样的,根本就不会继续判断,执行了添加操作。 */ 你使用的是HashSet集合,这个集合的底层是哈希表结构。 而哈希表结构底层依赖:hashCode()和equals()方法。 如果你认为对象的成员变量值相同即为同一个对象的话,你就应该重写这两个方法。 如何重写呢? 不同担心,自动生成即可。
HashSet集合存储数据的结构
- JDK的版本不同,HashSet集合的数据结构有所不同:
- JDK8之前:数组+链表
- JDK8之后:数组+链表+红黑树
以上数据结构我们称之为是哈希表
什么是哈希表
在JDK1.8之前,哈希表底层采用数组+链表实现,即使用链表处理冲突,同一hash值的链表都存储在一个链表里。但是当位于一个桶中的元素较多,即hash值相等的元素较多时,通过key值依次查找的效率较低。而JDK1.8中,哈希表存储采用数组+链表+红黑树实现,当链表长度超过阈值(8)时,将链表转换为红黑树,这样大大减少了查找时间。
简单的来说,哈希表是由数组+链表+红黑树(JDK1.8增加了红黑树部分)实现的,如下图所示。
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-BrUtgqNR-1672475567266)(assets/哈希表.png)]
- 存储流程分析:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-XWVL17u8-1672475567267)(assets/哈希流程图.png)] - 总结
总而言之,JDK1.8引入红黑树大程度优化了HashMap的性能,那么对于我们来讲保证HashSet集合元素的唯一,其实就是根据对象的hashCode和equals方法来决定的。【如果我们往集合中存放自定义的对象,那么保证其唯一,就必须复写hashCode和equals方法建立属于当前对象的比较方式】。
LinkedHashSet
什么是LinkedHashSet
我们知道HashSet保证元素唯一,可是元素存放进去是没有顺序的,那么我们要保证有序,怎么办呢?
在HashSet下面有一个子类java.util.LinkedHashSet
,它是 链表 和 哈希表 组合的一个数据存储结构。
LinkedHashSet集合的特点
- LinkedHashSet集合中的元素不可重复
- LinkedHashSet集合没有索引
- LinkedHashSet集合是有序的(存储元素的顺序与取出元素顺序一致)
总结: 有序,唯一
代码演示
public class LinkedHashSetDemo { public static void main(String[] args) { Set<String> set = new LinkedHashSet<String>(); set.add("bbb"); set.add("aaa"); set.add("abc"); set.add("bbc"); Iterator<String> it = set.iterator(); while (it.hasNext()) { System.out.println(it.next()); } } }
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-L1M6xkGO-1672475567268)(assets/image-20201119202807088.png)]
TreeSet
使用元素的自然排序对元素进行排序 或者根据创建set时提供的Comparable排序 具体取决于你用的构造方法
TreeSet自然排序
代码实现
public class TreeSetDemo { public static void main(String[] args) { //使用元素的自然顺序对元素进行排序,唯一 TreeSet<Integer> ts = new TreeSet<>(); ts.add(20); ts.add(18); ts.add(23); ts.add(22); ts.add(17); ts.add(24); ts.add(19); ts.add(18); ts.add(24); for(Integer i : ts){ System.out.println(i); } System.out.println("================="); TreeSet<String> ts2 = new TreeSet<>(); ts2.add("ab"); ts2.add("e"); ts2.add("r"); ts2.add("y"); ts2.add("c"); ts2.add("ac"); for(String s : ts2){ System.out.println(s); } } }
TreeSet存储自定义对象
public class Demo { public static void main(String[] args) { // 创建集合对象 TreeSet<Student> ts = new TreeSet<Student>(); Student s1 = new Student("b",23); Student s2 = new Student("a",23); Student s3 = new Student("jack",27); ts.add(s1); ts.add(s2); ts.add(s3); for(Student s : ts){ System.out.println(s.getName()+"--"+s.getAge()); } } }
/** * @Auther: yanqi * @Date: 14:40 * @Desc 如果一个类的元素要想进行自然排序,就必须实现自然排序的接口 Comparable 可以看成是内部比较器,需要修改原有代码,不符合OCP原则 */ public class Student implements Comparable<Student>{ private String name; private int age; public Student() { } public Student(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; } @Override public String toString() { return "Student{" + "name='" + name + '\'' + ", age=" + age + '}'; } @Override public int compareTo(Student s) { //按照年龄排序 ,主要条件 int num = this.age - s.age;//年龄相同就不存储 int num2 = num == 0 ? this.name.compareTo(s.name) : num ;//年龄相同的的时同,比较一下名是否相同 return num2; } }
JAVA零基础小白学习免费教程day14-Set&HashMap(二)https://developer.aliyun.com/article/1433779