目录
Set集合是一个不包含重复元素的collection,更确切地讲,set 不包含满足 e1.equals(e2)
的元素对 e1 和 e2 ,并且最多包含一个 null 元素。
HashSet
HashSet的特点
1)底层数据结构是哈希表。Hash线程不安全,集合元素可以是 null 。
哈希表是一个元素为链表的数组,综合了数组和链表的优点。
2)当向 HashSet 集合中存入一个元素时,HashSet 会调用该对象的 hashCode() 方法来得到该对象的 hashCode 值,然后根据 hashCode 值决定该对象在 HashSet 中的存储位置。
3)HashSet 集合判断两个元素相等的标准:两个对象通过 hashCode() 方法比较相等,并且两个对象的 equals() 方法返回值也相等。
4)HashSet 保证元素唯一性是靠元素重写 hashCode() 和 equals() 方法来保证的,如果不重写则无法保证。
HashSet存储自定义对象并保证元素的唯一性
定义的Student类
import java.util.Objects;
public class Student {
private String name;
private int age;
public Student(String name, int age) {
this.name = name;
this.age = age;
}
@Override
public String toString() {
return "\""+this.name+
"\""+","+this.age ;
}
@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);
}
}
测试类
import java.util.HashSet;
public class Test {
public static void main(String[] args) {
HashSet<Student> hashSet = new HashSet<>();
hashSet.add(new Student("Alice", 18));
hashSet.add(new Student("Jack", 17));
hashSet.add(new Student("Jack", 18));
hashSet.add(new Student("Helen", 19));
hashSet.add(new Student("David", 22));
System.out.println(hashSet);
}
}
LinkedHashSet
数据结构
链表和哈希表。链表保证元素的有序(和输入的顺序保持一致),哈希表保证元素的唯一。
所以 LinkedHashSet 的特点有:元素有序且唯一。
案例
实现 {21, 54, 23, 54, 21, 8, 2, 8, 23, 65} 存入 LinkedHashSet 集合,然后遍历集合验证元素是否有序且唯一。
import java.util.LinkedHashSet;
public class Test2 {
public static void main(String[] args) {
//{21, 54, 23, 54, 21, 8, 2, 8, 23, 65}
LinkedHashSet<Integer> linkedHashSet = new LinkedHashSet<>();
linkedHashSet.add(21);
linkedHashSet.add(54);
linkedHashSet.add(23);
linkedHashSet.add(54);
linkedHashSet.add(21);
linkedHashSet.add(8);
linkedHashSet.add(2);
linkedHashSet.add(8);
linkedHashSet.add(23);
linkedHashSet.add(65);
for (Integer i : linkedHashSet) {
System.out.print(i+"\t");
}
}
}
TreeSet
特点
元素唯一,并且可以对元素进行排序(自然排序,比较器排序)。
元素唯一性的保证是依靠compareTo()方法的返回值来确定的,如果返回0,则表示这两个元素相等,则不重复存储。
TreeSet集合自然排序
TreeSet集合中使用的是何种遍历方法,依据的是构造方法,如果是空参构造,那么就是自然排序;如果是有参构造,就是比较器排序。
使用TreeSet集合进行元素的自然排序,那么对元素有要求,要求这个元素必须实现Comparable接口 否则无法进行自然排序。
在Java API文档中我们会发现,常用的数据结构类,例如 Integer , String 等都实现了 Comparable 接口,所以在存储这些基本类型的对象时,不需要再考虑 Comparable 接口的问题。
例如在TreeSet集合中存入Integer类型的数据,然后遍历
import java.util.TreeSet;
public class Test {
public static void main(String[] args) {
TreeSet<Integer> treeSet = new TreeSet<>();
treeSet.add(20);
treeSet.add(18);
treeSet.add(23);
treeSet.add(22);
treeSet.add(17);
treeSet.add(24);
treeSet.add(19);
treeSet.add(18);
treeSet.add(24);
System.out.println(treeSet);
}
}
TreeSet集合比较器排序
设计Student类,实现Comparable接口并实现compareTo()方法
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 student) {
//按照姓名长度来排序
int num = this.name.length() - student.name.length();
//如果姓名长度一样,不能说明就是同一个对象,还得比较一下姓名的内容
int num2 = num == 0 ? this.name.compareTo(student.name) : num;
//如果姓名长度和内容一样,还得比较一下年龄是否一样
return num2 == 0 ? this.age - student.age : num2;
//根据返回值的 正 负 0 来决定元素在二叉树中放置的左右位置,0 就是不往里面放。
}
}
测试类中创建TreeSet集合,并存入Student对象
import java.util.TreeSet;
public class MyTest3 {
public static void main(String[] args) {
//比较器排序:
TreeSet<Student> treeSet = new TreeSet<>();
//存储学生对象,并且根据学生的姓名的长度进行排序
treeSet.add(new Student("张三", 23));
treeSet.add(new Student("李四", 23));
treeSet.add(new Student("李四", 24));
treeSet.add(new Student("刘洋asdfasf", 23));
treeSet.add(new Student("李四asfasfasdf", 10));
treeSet.add(new Student("王五dd", 60));
treeSet.add(new Student("张三ddfdfd", 23));
treeSet.add(new Student("赵六dfdfd", 19));
treeSet.add(new Student("田七dfdf", 28));
//增强for循环遍历集合
for (Student student : treeSet) {
System.out.println(student);
}
}
}
TreeSet集合保证元素唯一和自然排序的原理
TreeSet存储元素时采用的方法是二叉树存储,第一个放入的元素是根,后来的元素和根比较,小的放左边,大的放右边,相同的不存储。取的时候按照左中右的顺序,刚好实现了有序的特性。
例如,将 20, 18, 23, 22, 17, 24, 19, 18, 24 存入TreeSet集合中,
案例
产生10个不重复的1-20之间的随机整数
思路:
1)要产生随机数,可以使用 Math 工具类的random()方法来产生随机数,然后只需要对产生的随机数进行处理即可达到1-20之间的要求。
2)要使产生的随机数不能重复,可以将产生的随机数放在HashSet或者TreeSet集合中,推荐使用TreeSet集合,因为即保证了元素唯一性,又可以进行自然排序,输出时对结果一目了然。
3)因为生成的随机数可能重复,所以选择使用while循环或者递归来完成。
import java.util.TreeSet;
public class Test2 {
public static void main(String[] args) {
//创建TreeSet集合
TreeSet<Integer> treeSet = new TreeSet<>();
while (treeSet.size() < 10) {
//生成1-100随机整数
int ele = (int) (Math.random() * 100) + 1;
if (ele > 0 && ele <= 20) {
treeSet.add(ele);
}
}
//增强for遍历集合
for (Integer integer : treeSet) {
System.out.print(integer+"\t");
}
}
}
键盘录入学生信息按照总分排序后输出在控制台
思路:
1)首先要自定义Student类,成员变量要有 姓名name, 语文成绩chineseScore, 数学成绩mathScore, 英语成绩englishScore ,需要使用总分进行排序,那么可以定义一个 总分totalScore 方便排序时进行比较。
2)创建TreeSet集合,因为TreeSet集合的元素唯一且有序,简化了去重的一个步骤;而有序,我们需要设计比较器来完成。
3)创建扫描器对象,将数据输入存储进集合,遍历集合。
Student类:
public class Student {
private String name;
private int chineseScore;
private int mathScore;
private int englishScore;
public Student(String name, int chineseScore, int mathScore, int englishScore) {
this.name = name;
this.chineseScore = chineseScore;
this.mathScore = mathScore;
this.englishScore = englishScore;
}
public int getTotalScore() {
return this.chineseScore
+this.englishScore
+this.mathScore;
}
public String getName() {
return name;
}
public int getChineseScore() {
return chineseScore;
}
public int getMathScore() {
return mathScore;
}
public int getEnglishScore() {
return englishScore;
}
@Override
public String toString() {
return "Student{" +
"name='" + name + '\'' +
", chineseScore=" + chineseScore +
", mathScore=" + mathScore +
", englishScore=" + englishScore +
", totalScore=" + getTotalScore()+
'}';
}
}
测试类:
import java.util.Comparator;
import java.util.Scanner;
import java.util.TreeSet;
public class Test3 {
public static void main(String[] args) {
//创建集合并传入比较器
TreeSet<Student> treeSet = new TreeSet<>(new Comparator<Student>() {
@Override
public int compare(Student s1, Student s2) {
return -(s1.getTotalScore()-s2.getTotalScore());
}
});
Scanner sc = new Scanner(System.in);
//键盘录入元素,这里以3个元素为例
for (int i = 1; i <= 3; i++) {
treeSet.add(new Student(sc.next(),sc.nextInt(),sc.nextInt(),sc.nextInt()));
}
//增强for遍历集合
for (Student student : treeSet) {
System.out.println(student);
}
}
}