Java --> Set系列集合
- HashSet、LinkedHashSet
1 public static void main(String[] args) {
2 //Set系列集合:HashSet、LinkedHashSet、TreeSet
3 //多态写法
4 //Set<String> sets = new HashSet<>(); //HashSet:无序、不重复、无索引
5 Set<String> sets = new LinkedHashSet<>(); // LinkedHashSet:有序、不重复、无索引
6 sets.add("Java");
7 sets.add("C语言");
8 sets.add("数据结构");
9 sets.add("Java");
10 sets.add("HTML");
11 sets.add("CSS");
12 System.out.println(sets);
13 }
HashSet示例运行结果:
LinkedHashSet示例运行结果:
- 相同对象的哈希值是一样的,不同对象的哈希值是不同的(对象的哈希值一旦确定,便不会改变,即与运行次数无关):
可以看到,两次运行的结果是一样的。
- HashSet1.7版本原理解析:(数组+链表)
- JDK1.8之后开始HashSet原理解析:数组 + 链表 + 红黑树 ,众所周知,当JDK1.8之前,如上边的1.7而言,当多个元素的哈希值相等时,改地址的元素就会变成链表,而我们知道,一旦链表的长度过长,便会使得元素的删除和查询性能大大降低。当链表的长度大于8的时候,便会自动转换为红黑树。有关红黑树的具体知识大家可参考这篇文章,个人感觉算是介绍的非常详细的一篇了:
- https://www.zhihu.com/question/312327402/answer/1263993419
- 案例:Set集合去重复
- 需求:创建一个存储学生对象的集合,存储多个学生对象,使用程序实现在控制台遍历改集合,要求:学生对象的成员变量值相同,我们就认为时同一个对象。
实现步骤分析:
- 定义学生类,创建HashSet集合对象,创建学生对象;
- 把学生添加到集合中;
- 重点:在学生列中重写两个方法 -- hashCode()和equals(),自动生成即可;
- 遍历集合(增强for)。
学生类:
1 public class Student {
2 private String name;
3 private int age;
4 private char sex;
5
6 @Override
7 public String toString() {
8 return "Student{" +
9 "name='" + name + '\'' +
10 ", age=" + age +
11 ", sex=" + sex +
12 '}';
13 }
14
15 @Override
16 public boolean equals(Object o) {
17 if (this == o) return true;
18 if (o == null || getClass() != o.getClass()) return false;
19 Student student = (Student) o;
20 return age == student.age && sex == student.sex && Objects.equals(name, student.name);
21 }
22
23 @Override
24 public int hashCode() {
25 return Objects.hash(name, age, sex);
26 }
27
28 public Student() {
29 }
30
31 public Student(String name, int age, char sex) {
32 this.name = name;
33 this.age = age;
34 this.sex = sex;
35 }
36
37 public String getName() {
38 return name;
39 }
40
41 public void setName(String name) {
42 this.name = name;
43 }
44
45 public int getAge() {
46 return age;
47 }
48
49 public void setAge(int age) {
50 this.age = age;
51 }
52
53 public char getSex() {
54 return sex;
55 }
56
57 public void setSex(char sex) {
58 this.sex = sex;
59 }
60 }
1 public static void main(String[] args) {
2 //Set集合去重复原因:先判断哈希值,在判断equals
3 Set<Student> stus = new HashSet<>();
4 Student s1 = new Student("Jack",21,'男');
5 Student s2 = new Student("Jack",21,'男');
6 Student s3 = new Student("Rose",20,'女');
7
8 System.out.println(s1.hashCode());
9 System.out.println(s2.hashCode());
10 System.out.println(s3.hashCode());
11
12 stus.add(s1);
13 stus.add(s2);
14 stus.add(s3);
15
16 System.out.println(stus);
17 }
示例运行结果:
通过重写对象的hashCode和equals方法,可以实现Set集合中2个内容相同的对象就是重复的。
示例运行结果:
底层原理:基于哈希表,每个元素额外多了一个双链表的机制记录存储的顺序
- 实现类:TreeSet
- 不重复、无索引、可排序
- 案例:TreeSet集合的比较规则(数值型、字符串型、以及自定义类型):
Cat类:
1 public class Cat implements Comparable<Cat>{ //实现Comparable接口,重写compareTo方法可实现自定义比较规则
2 private String name;
3 private String color;
4 private int age;
5 private double weight;
6
7 //类自定义比较规则
8 @Override
9 public int compareTo(Cat o) {
10 return this.age - o.age; //年龄升序(反之年龄降序)
11 //return (this.age - o.age >= 0) ? 1 : -1; //保留相同对象
12 //return Double.compare(this.weight,o.weight); //体重升序(反之体重降序)
13 //return (this.weight - o.weight >= 0) ? 1 : -1; //保留相同对象
14 }
15
16 @Override
17 public String toString() {
18 return "Cat{" +
19 "name='" + name + '\'' +
20 ", color='" + color + '\'' +
21 ", age=" + age +
22 ", weight=" + weight +
23 '}';
24 }
25
26 public Cat() {
27 }
28
29 public Cat(String name, String color, int age, double weight) {
30 this.name = name;
31 this.color = color;
32 this.age = age;
33 this.weight = weight;
34 }
35
36 public String getName() {
37 return name;
38 }
39
40 public void setName(String name) {
41 this.name = name;
42 }
43
44 public String getColor() {
45 return color;
46 }
47
48 public void setColor(String color) {
49 this.color = color;
50 }
51
52 public int getAge() {
53 return age;
54 }
55
56 public void setAge(int age) {
57 this.age = age;
58 }
59
60 public double getWeight() {
61 return weight;
62 }
63
64 public void setWeight(double weight) {
65 this.weight = weight;
66 }
67 }
1 public static void main(String[] args) {
2 System.out.println("-----------数值型-------------");
3 Set<Integer> sets = new TreeSet<>(); //不重复、无索引、可排序
4 sets.add(18);
5 sets.add(19);
6 sets.add(21);
7 sets.add(5);
8 System.out.println(sets);
9
10 System.out.println("-------------字符串型(ASCII)--------------");
11 Set<String> sets2 = new TreeSet<>();
12 sets2.add("Java");
13 sets2.add("HTML");
14 sets2.add("CSS");
15 sets2.add("MySQL");
16 sets2.add("HTML");
17 sets2.add("java");
18 sets2.add("棒");
19 System.out.println(sets2);
20
21 System.out.println("------------自定义比较规则-------------");
22 Set<Cat> cats = new TreeSet<>();
23 cats.add(new Cat("大橘猫","橘色",2,2.5));
24 cats.add(new Cat("狸花猫","狸色",1,1.5));
25 cats.add(new Cat("小黑","黑色",3,3.5));
26 cats.add(new Cat("小黑","黑色",3,3.5));
27 System.out.println(cats);
28 }
示例运行结果:
以上便是在类中实现Comparable接口,在类中重写comparaTo方法的方式实现了TreeSet集合的自定义排序。
- 方式二:TreeSet集合自带比较器对象进行规则定制
可以看到:集合内部的比较器会优先使用。还可以使用Lambda表达式简化函数式接口的匿名内部类:
对此,我们对Collection体系的集合做一个简答的总结:
- 如果希望元素可以重复,又有索引,索引查询快时可用ArrayList集合,因为它时基于数组的。(用的最多);
- 如果希望元素可以重复,又有索引,增删首位操作快则可以使用LinkedList集合,基于链表;
- 如果希望增删改查相对都较快,但是元素不重复、无序、无索引,可以使用HashSet,基于哈希表
- 如果希望增删改查都快,但是元素不重复、有序、无索引,可以使用LinkedHashSet,基于哈希表和双链表;
- 如果希望对对象进行排序,可以使用TreeSet集合,基于红黑树。后续也可以使用List集合进行排序。