1.Set集合概述和特点

Set集合特点

  • 不包含重复元素的集合
  • 没有带索引的方法,所以不能使用普通for循环遍历

Set集合练习

  • 存储字符串并遍历
  1. package com.study_01;
  2. import java.util.HashSet;
  3. import java.util.Set;
  4. public class SetDemo {
  5. public static void main(String[] args) {
  6. // 创建集合对象
  7. Set<String> set = new HashSet<String>();
  8. set.add("hello");
  9. set.add("world");
  10. set.add("java");
  11. // 不包含重复的元素
  12. set.add("hello");
  13. // 遍历
  14. for (String s : set) {
  15. System.out.println(s);
  16. }
  17. }
  18. }

2.哈希值

哈希值:是JDK根据对象的地址或者字符串或者数字算出来的int类型的数值

Object类中有一个方法可以获取对象的哈希值

  • public int hashCode():返回对象的哈希码值

对象的哈希值特点

  • 同一个对象多次调用hashCode()方法返回的哈希值是相同的
  • 默认情况下,不同对象的哈希值是不同的。而重写hashCode()方法,可以实现让不同对象的哈希值相同
  1. package com.study_02;
  2. public class HashDemo {
  3. public static void main(String[] args) {
  4. // 创建学生对象
  5. Student s1 = new Student("小黑", 36);
  6. // 同一个对象多次调用 hashCode()方法返回的哈希值是相同的
  7. System.out.println(s1.hashCode());// 2083562754
  8. System.out.println(s1.hashCode());// 2083562754
  9. System.out.println("-----");
  10. Student s2 = new Student("小黑", 36);
  11. // 默认情况下,不同对象的哈希值是不相同的
  12. // 通过方法重写可以实现不同对象的哈希值是相同的
  13. System.out.println(s2.hashCode());// 557041912
  14. System.out.println("-----");
  15. System.out.println("hello".hashCode());
  16. System.out.println("world".hashCode());
  17. System.out.println("java".hashCode());
  18. System.out.println("hello".hashCode());
  19. System.out.println("-----");
  20. System.out.println("重地".hashCode());// 1179395
  21. System.out.println("通话".hashCode());// 1179395
  22. System.out.println("你好".hashCode());// 652829
  23. }
  24. }
  1. package com.study_02;
  2. public class Student {
  3. private String name;
  4. private int age;
  5. public Student(String name, int age) {
  6. this.name = name;
  7. this.age = age;
  8. }
  9. public Student() {
  10. }
  11. public String getName() {
  12. return name;
  13. }
  14. public void setName(String name) {
  15. this.name = name;
  16. }
  17. public int getAge() {
  18. return age;
  19. }
  20. public void setAge(int age) {
  21. this.age = age;
  22. }
  23. @Override
  24. public int hashCode() {
  25. return super.hashCode();
  26. }
  27. }

3.HashSet的概述和特点

HashSet集合特点

  • 底层数据结构是哈希表
  • 对集合的迭代顺序不做任何保证,也就是说不保证存储和取出的元素顺序一致
  • 没有带索引的方法,所以不能使用普遍for循环
  • 由于是Set集合,所以是不包含重复元素的集合

HashSet集合练习

  • 存储字符串并遍历
  1. package com.study_03;
  2. import java.util.HashSet;
  3. public class HashSetDemo {
  4. public static void main(String[] args) {
  5. // 创建集合对象
  6. HashSet<String> hs = new HashSet<>();
  7. // 添加元素
  8. hs.add("hello");
  9. hs.add("world");
  10. hs.add("java");
  11. hs.add("world");
  12. // 遍历
  13. for(String s : hs) {
  14. System.out.println(s);
  15. }
  16. }
  17. }

4.HashSet集合保证元素唯一性源码分析

HashSet集合添加一个元素的过程

Set - 图1

HashSet集合存储元素:

  • 要保证元素唯一性,需要重写hashCode()和equals()

默认初始长度为16

  1. package com.study_03;
  2. import java.util.HashSet;
  3. public class HashSetDemo02 {
  4. public static void main(String[] args) {
  5. //创建HashSet集合对象
  6. HashSet<Student> hs = new HashSet<>();
  7. // 创建学生对象
  8. Student s1 = new Student("林青霞",30);
  9. Student s2 = new Student("张曼玉",35);
  10. Student s3 = new Student("王祖贤",31);
  11. Student s4 = new Student("王祖贤",31);
  12. // 把学生添加到集合
  13. hs.add(s1);
  14. hs.add(s2);
  15. hs.add(s3);
  16. hs.add(s4);
  17. // 遍历集合,增强for
  18. for(Student s : hs) {
  19. System.out.println(s);
  20. }
  21. }
  22. }
  1. package com.study_03;
  2. import java.util.Objects;
  3. public class Student {
  4. private String name;
  5. private int age;
  6. public Student(String name, int age) {
  7. this.name = name;
  8. this.age = age;
  9. }
  10. public Student() {
  11. }
  12. public String getName() {
  13. return name;
  14. }
  15. public void setName(String name) {
  16. this.name = name;
  17. }
  18. public int getAge() {
  19. return age;
  20. }
  21. public void setAge(int age) {
  22. this.age = age;
  23. }
  24. @Override
  25. public boolean equals(Object o) {
  26. if (this == o) return true;
  27. if (o == null || getClass() != o.getClass()) return false;
  28. Student student = (Student) o;
  29. return age == student.age && Objects.equals(name, student.name);
  30. }
  31. @Override
  32. public int hashCode() {
  33. return Objects.hash(name, age);
  34. }
  35. @Override
  36. public String toString() {
  37. return "Student{" +
  38. "name='" + name + '\'' +
  39. ", age=" + age +
  40. '}';
  41. }
  42. }

5.常见数据结构之哈希表

哈希表

  • JDK8之前,底层采用数组+链表实现,可以说是一个元素为链表的数组
  • JDK8以后,在长度比较长的时候,底层实现了优化

Set - 图2

6.学生案例

创建一个存储学生对象的集合,存储多个学生对象,使用程序实现在控制台遍历该集合要求:学生对象的成员变量值相同,我们就认为是

同一个对象

在学生类中重写两个方法hashCode()和equals),自动生成即可

7.LinkedHashSet集合的概述和特点

LinkedHashSet集合特点

  • 哈希表和链表实现的Set接口,具有可预测的迭代次序
  • 由链表保证元素有序,也就是说元素的存储和取出顺序是一致的
  • 由哈希表保证元素唯一,也就是没有重复的元素

LinkedHashSet集合练习

  • 存储字符串并遍历
  1. package com.study_04;
  2. import java.util.LinkedHashSet;
  3. public class LinkedHashSetDemo {
  4. public static void main(String[] args) {
  5. // 创建集合对象
  6. LinkedHashSet<String> linkedHashSet = new LinkedHashSet<>();
  7. // 添加元素
  8. linkedHashSet.add("hello");
  9. linkedHashSet.add("world");
  10. linkedHashSet.add("java");
  11. linkedHashSet.add("world");
  12. // 遍历集合
  13. for (String s : linkedHashSet) {
  14. System.out.println(s);
  15. }
  16. }
  17. }

8.TreeSet集合概述和特点

间接的实现了Set接口

TreeSet集合特点

  • 元素有序,这里的顺序不是指存储和取出的顺序,而是按照一定的规则进行排序,具体排序方式取决于构造方法
    • TreeSet():根据其元素的自然排序进行排序
    • TreeSet(Comparator comparator):根据指定的比较器进行排序
  • 没有带索引的方法,所以不能使用普通for循环遍历
  • 由于是Set集合,所以不包含重复元素的集合

TreeSet集合练习

  • 存储整数并遍历
  1. package com.study_05;
  2. import java.util.TreeSet;
  3. public class TreeSetDemo01 {
  4. public static void main(String[] args) {
  5. // 创建集合对象
  6. TreeSet<Integer> ts = new TreeSet<>();
  7. // 添加元素
  8. ts.add(10);
  9. ts.add(30);
  10. ts.add(50);
  11. ts.add(20);
  12. ts.add(30);
  13. // 遍历集合
  14. for (Integer i : ts) {
  15. System.out.println(i);
  16. }
  17. }
  18. }

9.自然排序Comparable

  • 存储学生对象并遍历,创建TreeSet集合使用无参构造方法
  • 要求:按照年龄从小到大排序,年龄相同时,按照姓名的字母顺序排序

结论

  • 用TreeSet集合存储自定义对象,无参构造方法使用的是自然排序对元素进行排序的
  • 自然排序,就是让元素所属的类实现Comparable接口,重写compareTo(T o)方法
  • 重写方法时,一定要注意排序规则必须按照要求的主要条件和次要条件来写
  1. @Override
  2. public int compareTo(Student s) {
  3. // return 1;
  4. // return 0;
  5. // return -1;
  6. // 按照年龄从小到大排序
  7. int num = this.age - s.age;
  8. // int num = s.age - this.age;
  9. // 年龄相同时,按照姓名字母顺序排序
  10. int num2 = num == 0 ? this.name.compareTo(s.name):num;
  11. return num2;
  12. }
  1. package com.study_05;
  2. import java.util.TreeSet;
  3. public class TreeSetDemo02 {
  4. public static void main(String[] args) {
  5. // 创建集合对象
  6. TreeSet<Student> ts = new TreeSet<>();
  7. // 创建学生对象
  8. Student s1 = new Student("林青霞",30);
  9. Student s2 = new Student("张曼玉",35);
  10. Student s3 = new Student("王祖贤",31);
  11. Student s4 = new Student("xiaoli",31);
  12. Student s5 = new Student("xiaoli",31);
  13. // 添加到集合
  14. ts.add(s1);
  15. ts.add(s2);
  16. ts.add(s3);
  17. ts.add(s4);
  18. ts.add(s5);
  19. // 遍历集合
  20. for (Student s : ts) {
  21. System.out.println(s);
  22. }
  23. }
  24. }
  1. package com.study_05;
  2. import java.util.Objects;
  3. public class Student implements Comparable<Student>{
  4. private String name;
  5. private int age;
  6. public Student(String name, int age) {
  7. this.name = name;
  8. this.age = age;
  9. }
  10. public Student() {
  11. }
  12. public String getName() {
  13. return name;
  14. }
  15. public void setName(String name) {
  16. this.name = name;
  17. }
  18. public int getAge() {
  19. return age;
  20. }
  21. public void setAge(int age) {
  22. this.age = age;
  23. }
  24. @Override
  25. public int compareTo(Student s) {
  26. // return 1;
  27. // return 0;
  28. // return -1;
  29. // 按照年龄从小到大排序
  30. int num = this.age - s.age;
  31. // int num = s.age - this.age;
  32. // 年龄相同时,按照姓名字母顺序排序
  33. int num2 = num == 0 ? this.name.compareTo(s.name):num;
  34. return num2;
  35. }
  36. @Override
  37. public String toString() {
  38. return "Student{" +
  39. "name='" + name + '\'' +
  40. ", age=" + age +
  41. '}';
  42. }
  43. }

10.比较器排序Comparator的使用

  • 存储学生对象并遍历,创建TreeSet集合使用带参构造方法
  • 要求:按照年龄从小到大排序,年龄相同时,按照暇姓名的字母顺序排序

结论

  • 用TreeSet集合存储自定义对象,带参构造方法使用的是比较器排序对元素进行排序的
  • 比较器排序,就是让集合构造方法接收Comparator的实现类对象,重写compare(T o1,T o2)方法重写方法时,一定要注意排序规则必须按照要求的主要条件和次要条件来写
  1. // 创建集合对象
  2. TreeSet<Student> ts = new TreeSet<Student>(new Comparator<Student>() {
  3. @Override
  4. public int compare(Student s1, Student s2) {
  5. // this.age - s.age
  6. // s1,s2
  7. int num = s1.getAge() - s2.getAge();
  8. int num2 = num == 0 ? s1.getName().compareTo(s2.getName()) : num;
  9. return num2;
  10. }
  11. });
  1. package com.study_06;
  2. import java.util.Comparator;
  3. import java.util.TreeSet;
  4. public class TreeSetDemo01 {
  5. public static void main(String[] args) {
  6. // 创建集合对象
  7. TreeSet<Student> ts = new TreeSet<Student>(new Comparator<Student>() {
  8. @Override
  9. public int compare(Student s1, Student s2) {
  10. // this.age - s.age
  11. // s1,s2
  12. int num = s1.getAge() - s2.getAge();
  13. int num2 = num == 0 ? s1.getName().compareTo(s2.getName()) : num;
  14. return num2;
  15. }
  16. });
  17. // 创建学生对象
  18. Student s1 = new Student("林青霞", 30);
  19. Student s2 = new Student("张曼玉", 35);
  20. Student s3 = new Student("王祖贤", 31);
  21. Student s4 = new Student("xiaoli", 31);
  22. Student s5 = new Student("xiaoli", 31);
  23. ts.add(s1);
  24. ts.add(s2);
  25. ts.add(s3);
  26. ts.add(s4);
  27. ts.add(s5);
  28. for (Student s : ts) {
  29. System.out.println(s);
  30. }
  31. }
  32. }
  1. package com.study_06;
  2. public class Student {
  3. private String name;
  4. private int age;
  5. public Student(String name, int age) {
  6. this.name = name;
  7. this.age = age;
  8. }
  9. public Student() {
  10. }
  11. public String getName() {
  12. return name;
  13. }
  14. public void setName(String name) {
  15. this.name = name;
  16. }
  17. public int getAge() {
  18. return age;
  19. }
  20. public void setAge(int age) {
  21. this.age = age;
  22. }
  23. @Override
  24. public String toString() {
  25. return "Student{" +
  26. "name='" + name + '\'' +
  27. ", age=" + age +
  28. '}';
  29. }
  30. }

11.Set案例

成绩排序:

需求:用TreeSet集合存储多个学生信息(姓名,语文成绩,数学成绩),并遍历该集合

要求:按照总分从高到低出现

  1. package com.study_07;
  2. import java.util.Comparator;
  3. import java.util.TreeSet;
  4. public class TreeSetDemo01 {
  5. public static void main(String[] args) {
  6. // 创建TreeSet集合对象通过比较器进行排序
  7. TreeSet<Student> ts = new TreeSet<>(new Comparator<Student>() {
  8. @Override
  9. public int compare(Student s1, Student s2) {
  10. int num = s1.getSum() - s2.getSum();
  11. int num2 = num == 0 ? s1.getChinese() - s2.getChinese() : num;
  12. int num3 = num2 == 0 ? s1.getName().compareTo(s2.getName()) : num2;
  13. return num3;
  14. }
  15. });
  16. // 创建学生对象
  17. Student s1 = new Student("林青霞", 98, 100);
  18. Student s2 = new Student("张曼玉", 95, 95);
  19. Student s3 = new Student("王祖贤", 100, 93);
  20. Student s4 = new Student("柳岩", 100, 97);
  21. Student s5 = new Student("风清扬", 98, 98);
  22. Student s6 = new Student("左冷禅", 97, 99);
  23. // Student s7 = new Student("左冷禅",97,99);
  24. Student s7 = new Student("赵云", 97, 99);
  25. ts.add(s1);
  26. ts.add(s2);
  27. ts.add(s3);
  28. ts.add(s4);
  29. ts.add(s5);
  30. ts.add(s6);
  31. ts.add(s7);
  32. for (Student s : ts) {
  33. System.out.println(s);
  34. }
  35. }
  36. }
  1. package com.study_07;
  2. public class Student {
  3. private String name;
  4. private int chinese;
  5. private int math;
  6. public Student(String name, int chinese, int math) {
  7. this.name = name;
  8. this.chinese = chinese;
  9. this.math = math;
  10. }
  11. public Student() {
  12. }
  13. public String getName() {
  14. return name;
  15. }
  16. public void setName(String name) {
  17. this.name = name;
  18. }
  19. public int getChinese() {
  20. return chinese;
  21. }
  22. public void setChinese(int chinese) {
  23. this.chinese = chinese;
  24. }
  25. public int getMath() {
  26. return math;
  27. }
  28. public void setMath(int math) {
  29. this.math = math;
  30. }
  31. public int getSum() {
  32. return this.chinese+this.math;
  33. }
  34. @Override
  35. public String toString() {
  36. return "Student{" +
  37. "name='" + name + '\'' +
  38. ", chinese=" + chinese +
  39. ", math=" + math +
  40. ", sum=" + this.getSum() +
  41. '}';
  42. }
  43. }

12.不重复的随机数案例

案例:不重复的随机数
需求:编写一个程序,获取10个1-20之间的随机数,要求随机数不能重复,并在控制台输出

  1. package com.study_08;
  2. import java.util.HashSet;
  3. import java.util.Random;
  4. import java.util.Set;
  5. import java.util.TreeSet;
  6. public class SetDemo {
  7. public static void main(String[] args) {
  8. // 创建Set集合对象
  9. // Set<Integer> set = new HashSet<Integer>();
  10. Set<Integer> set = new TreeSet<Integer>(); // 有序的
  11. // 创建随机数对象
  12. Random r = new Random();
  13. // 判断集合承兑是不是小于0
  14. while (set.size() < 10) {
  15. /*
  16. * 返回伪随机数,从0(包括)和指定值(排除)之间均匀分布的int值,从该随机数生成器的序列中抽取出来。
  17. * nextInt的一般合同是指定范围内的一个int值被伪随机生成并返回
  18. * */
  19. int number = r.nextInt(20) + 1;
  20. set.add(number);
  21. }
  22. // 遍历集合
  23. for(Integer i : set) {
  24. System.out.println(i);
  25. }
  26. }
  27. }