目录
1.Set接口概述
一个不包含重复元素的Collection。
2.HashSet集合
2.1 概述
- 不保证set的迭代顺序
- 特别是它不保证该顺序恒久不变
2.2 HashSet如何保证元素唯一性
- HashSet集合底层依赖哈希表结构
- 而哈希表结构底层依赖hashcode()方法和equals()方法
2.3 HashSet案例
2.3.1 HashSet存储字符串并遍历
HashSet存储字符串并遍历,输出结果元素唯一,是因为String类重写了hashcode()方法和equals()方法,所以可以把内容相同的字符串去掉,保证元素唯一性。
底层原理分析如下:
- 首先比较hashcode()值是否相同
- 如果相同,继续走equals()方法
- 返回true,说明元素重复,就不添加
- 返回false,说明元素不重复,就添加到集合中
- 如果不同,直接把元素添加到集合
// 创建集合对象
HashSet<String> hs = new HashSet<String>();
// 添加元素
hs.add("Python");
hs.add("Fiddler");
hs.add("Selenium");
hs.add("Fiddler");
// 增强for遍历集合
for (String str : hs) {
System.out.println(str);
}
运行结果:
Selenium
Fiddler
Python
2.3.2 HashSet存储自定义对象并遍历
- Student(name, age)
- 要求:如果两个对象的成员变量值都相同,则为同一个元素。
Student.java的代码如下:
package com.hw.set;
public class Student {
private String name;
private int age;
public Student() {
super();
}
public Student(String name, int age) {
super();
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;
}
}
测试代码如下:
// 创建集合对象
HashSet<Student> hs = new HashSet<Student>();
// 创建学生对象
Student s1 = new Student("西施", 20);
Student s2 = new Student("貂蝉", 22);
Student s3 = new Student("昭君", 18);
Student s4 = new Student("貂蝉", 22);
// 把学生对象添加到集合中
hs.add(s1);
hs.add(s2);
hs.add(s3);
hs.add(s4);
// 增强for遍历集合
for (Student stu : hs) {
System.out.println(stu.getName() + "---" + stu.getAge());
}
运行结果如下:
西施---20
昭君---18
貂蝉---22
貂蝉---22
案例要求如果两个对象的成员变量值都相同,则为同一个元素,实际应该输出三个学生对象,然而四个学生对象全部输出了。原因是Student类没有重写hashcode()方法和equals()方法,默认使用的是Object类的这两个方法,这个时候学生的哈希值是不一样的,所以这四个学生对象会全部输出。
解决方法:在Student类中重写hashcode()方法和equals()方法。
重写hashcode()和equals()的Student.java的代码如下:
package com.hw.set;
public class Student {
private String name;
private int age;
public Student() {
super();
}
public Student(String name, int age) {
super();
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 int hashCode() {
final int prime = 31;
int result = 1;
result = prime * result + age;
result = prime * result + ((name == null) ? 0 : name.hashCode());
return result;
}
@Override
public boolean equals(Object obj) {
if (this == obj)
return true;
if (obj == null)
return false;
if (getClass() != obj.getClass())
return false;
Student other = (Student) obj;
if (age != other.age)
return false;
if (name == null) {
if (other.name != null)
return false;
} else if (!name.equals(other.name))
return false;
return true;
}
}
3.LinkedHashSet集合
- LinkedHashSet底层数据结构由哈希表和链表组成
- 由哈希表表示元素的唯一性
- 链表保证元素有序(存储顺序和取出顺序一致)
package com.hw.set;
import java.util.LinkedHashSet;
/**
* LinkedHashSet存储字符串并遍历
*
* @author HW
*
*/
public class LinkedHashSetDemo {
public static void main(String[] args) {
// 创建集合对象
LinkedHashSet<String> lhs = new LinkedHashSet<String>();
// 添加元素
lhs.add("Python");
lhs.add("Fiddler");
lhs.add("Selenium");
// 增强for循环遍历集合
for (String str : lhs) {
System.out.println(str);
}
}
}
运行结果如下:
Python
Fiddler
Selenium
4.TreeSet集合
TreeSet能够对元素按照某种规则进行排序。
排序有两种,使用哪一种方法,取决于使用的构造方法,当构造方法为空时,使用自然顺序进行排序。
- 自然排序:让元素所属的类实现自然排序接口Comparable<T>,下面先来了解一下Comparable<T>接口以及compareTo()方法:
- public interface Comparable<T>:此接口强行对实现它的每个类的对象进行整体排序,这种排序被称为类的自然排序。类的compareTo()方法被称为类的自然比较方法。
- int compareTo(T o):如果该对象小于、等于或大于指定对象,则分别返回负整数、零或正整数。
- 比较器排序:让集合的构造方法接收一个比较器接口的子类对象Comparator
TreeSet的底层是二叉树结构,保证元素排序和唯一。
4.1 TreeSet存储Integer类型的元素并遍历
// 创建集合对象
TreeSet<Integer> ts = new TreeSet<Integer>();
// 添加元素
ts.add(20);
ts.add(23);
ts.add(15);
ts.add(6);
ts.add(18);
ts.add(23);
// 增强for遍历集合
for (Integer i : ts) {
System.out.println(i);
}
运行结果:
6
15
18
20
23
4.2 TreeSet存储自定义对象并遍历
- Student(name, age)
- 按照年龄从小到大排序
4.2.1 使用自然排序
要想使用自然排序对学生对象排序,那么Student类就要实现Comparable<T>接口,并重写compareTo()方法。如果Student类没有实现Comparable<T>接口,运行结果就会报错:Exception in thread "main" java.lang.ClassCastException: com.hxs.day06.Student cannot be cast to java.lang.Comparable,如下所示:
下面在Student类中实现这一接口并重写compareTo()方法:
package com.hw.treeset;
/**
* 如果一个类的元素要想能够进行自然排序,就必须实现自然排序接口
*
* @author HW
*
*/
public class Student implements Comparable<Student> {
private String name;
private int age;
public Student() {
super();
}
public Student(String name, int age) {
super();
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;
}
public int compareTo(Student s) {
// 主要条件,按照年龄排序
int num = this.age - s.age;
// 次要条件,年龄相同的时候,比较姓名是否相同
// 如果年龄和姓名都相同,才是同一个元素
int num2 = num == 0 ? this.name.compareTo(s.name) : num;
return num2;
}
}
测试类代码如下:
package com.hw.treeset;
import java.util.TreeSet;
import com.hw.set.Student;
/**
* TreeSet存储自定义对象并遍历
* 自然排序,按照年龄从小到大排序
*
* @author HW
*
*/
public class TreeSetDemo2 {
public static void main(String[] args) {
// 创建集合对象
TreeSet<Student> ts = new TreeSet<Student>();
// 创建学生对象
Student s1 = new Student("linqingxia", 27);
Student s2 = new Student("zhangguorong", 29);
Student s3 = new Student("wanglihong", 23);
Student s4 = new Student("linqingxia", 27);
Student s5 = new Student("liushishi", 22);
Student s6 = new Student("wuqilong", 40);
Student s7 = new Student("fengqingy", 22);
// 把学生对象添加到集合中
ts.add(s1);
ts.add(s2);
ts.add(s3);
ts.add(s4);
ts.add(s5);
ts.add(s6);
ts.add(s7);
// 增强for遍历集合
for (Student stu : ts) {
System.out.println(stu.getName() + "---" + stu.getAge());
}
}
}
运行结果如下:
fengqingy---22
liushishi---22
wanglihong---23
linqingxia---27
zhangguorong---29
wuqilong---40
4.2.2 比较器排序
让集合的构造方法接收一个比较器接口的子类对象Comparator。
package com.hw.treeset;
import java.util.Comparator;
import java.util.TreeSet;
import com.hw.set.Student;
/**
* TreeSet存储自定义对象并遍历
* 比较器排序
*
* @author HW
*
*/
public class TreeSetDemo3 {
public static void main(String[] args) {
// 创建集合对象
// 如果一个方法的参数是接口,那么真正要的是接口的实现类的对象
// 而匿名内部类就可以实现这个东西
TreeSet<Student> ts = new TreeSet<Student>(new Comparator<Student>() {
public int compare(Student s1, Student s2) {
int num = s1.getAge() - s2.getAge();
int num2 = num == 0 ? s1.getName().compareTo(s2.getName())
: num;
return num;
}
});
// 创建学生对象
Student s1 = new Student("linqingxia", 27);
Student s2 = new Student("zhangguorong", 29);
Student s3 = new Student("wanglihong", 23);
Student s4 = new Student("linqingxia", 27);
Student s5 = new Student("liushishi", 22);
Student s6 = new Student("wuqilong", 40);
Student s7 = new Student("fengqingy", 22);
// 把学生对象添加到集合中
ts.add(s1);
ts.add(s2);
ts.add(s3);
ts.add(s4);
ts.add(s5);
ts.add(s6);
ts.add(s7);
// 增强for遍历集合
for (Student stu : ts) {
System.out.println(stu.getName() + "---" + stu.getAge());
}
}
}
运行结果如下:
liushishi---22
wanglihong---23
linqingxia---27
zhangguorong---29
wuqilong---40