1、set集合的概述
一个不包含重复元素的 collection。
- 案例演示:无需和唯一
public class MyTest {
public static void main(String[] args) {
HashSet<String> hashSet = new HashSet<>();
hashSet.add("张三");
hashSet.add("李四");
hashSet.add("王五");
hashSet.add("张三");
hashSet.add("李四");
for (String s : hashSet) {
System.out.println(s);
}
}
}
结果为:
2、HashSet
HashSet底层的数据结构是哈希表,HashSet不是线程安全的,集合元素可以是null。
哈希表:是一个元素为链表的数组,综合了数组和链表的优点(JDK1.7:数组+链表,JDK1.8优化后:数组+链表+二叉树 )
2.1、HashSet保证元素的唯一性
当向HashSet集合中存入一个元素时,HashSet会调用该对象的hashCOde()方法来得到该对象的hashCode值,然后根据hashCode值决定改对象在hashSet中存储位置。
hashSet集合判断两个元素相等的标准:
两个对象通过hashCode()方法比较相等,并且两个对象的equals()方法返回值也相等。
结论:HashSet保证元素唯一性是靠元素重写hashCode()和equals()方法来保证的,如果不重写则无法保证。
- hashCode()和equals()方法重写代码
public class MyTest2 {
public static void main(String[] args) {
HashSet<Student> hashSet = new HashSet<>();
Student s1 = new Student("张三", 26);
Student s2 = new Student("李四", 26);
Student s3 = new Student("王五", 26);
Student s4 = new Student("张三", 26);
Student s5 = new Student("张三", 26);
Student s6 = new Student("张三", 26);
hashSet.add(s1);
hashSet.add(s2);
hashSet.add(s3);
hashSet.add(s4);
hashSet.add(s5);
hashSet.add(s6);
for (Student student : hashSet) {
System.out.println(student.getName()+"=="+student.getAge());
}
}
}
public class Student {
private String name;
private int age;
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 int hashCode() {
//合理的重写hashCode()方法,可以减少碰撞次数,提高性能降低性能
//因为成员变量值影响了哈希值,所以将成员变量值相加即可
//return this.name.hashCode()+this.age;
//但是会存在下面问题
/*s1.name.hashCode()=40,age=30
s2.name.hashCode()=20,age=50
虽然成员变量不同,但是成员变量值相加下来相同。
所以我们为了区分,将他们随便乘一个整数
*/
return this.name.hashCode()+this.age*15;
}
@Override
public boolean equals(Object obj) {
if (this==obj){
return true;
}
if (!(obj instanceof Student)){
return false;
}
Student student= (Student) obj;
return this.name.equals(student.name) && this.age == student.age;
}
}
3、LinkedHashSet
元素有序,且唯一。
LinkedHashSet的概述:LinkedHashSet数据结构底层是两个链表和哈希表,元素唯一且有序。链表保证有序,哈希表保证唯一。
4、TreeSet
TreeSet集合的概述底层数据结构是二叉树,
TreeSet集合的特点:元素唯一,并且可以对元素进行排序。
排序方式:
- a、自然排序
- b、使用比较器排序
注意:使用TreeSet集合进行自然排序时,要求这个元素必须实现Comparable接口,否则无法进行排序
元素唯一性:靠comparTo方法的返回值来确定,如果返回0,表示两个元素相等,则不重复存储。 - 案例演示:存储Integer元素并遍历
public class MyTest3 {
public static void main(String[] args) {
TreeSet<Integer> set = new TreeSet<>();
set.add(20);
set.add(18);
set.add(23);
set.add(22);
set.add(17);
set.add(24);
set.add(19);
set.add(18);
set.add(24);
for (Integer integer : set) {
System.out.println(integer);
}
}
}
4.1保证元素唯一和自然排序的原理
二叉树的数据结构存储数据,先存入一个树根,分两个叉,存储元素时,跟树根比较,小的放左边,大的放右边,如果想等就不存储,取的时候按照左中右的顺序来取。
4.2、TreeSet存储自定义对象并遍历练习1
- 按照年龄进行排序
年龄就是主要条件,姓名就是次要条件
public class MyTest4 {
public static void main(String[] args) {
//根据年龄进行排序,年龄相同再比较姓名
TreeSet<Student> treeSet = new TreeSet<>();
treeSet.add(new Student("乔丹",40));
treeSet.add(new Student("科比",38));
treeSet.add(new Student("詹姆斯",30));
treeSet.add(new Student("姚明",33));
treeSet.add(new Student("易建联",23));
treeSet.add(new Student("王治郅",36));
treeSet.add(new Student("王仕鹏",27));
treeSet.add(new Student("孙悦",29));
treeSet.add(new Student("姚明1",33));
treeSet.add(new Student("易建联1",23));
treeSet.add(new Student("王治郅1",36));
for (Student student : treeSet) {
System.out.println(student);
}
}
}
重写compareTo()方法
@Override
public int compareTo(Student o) {
int num = this.age - o.age;//先比较年龄
//年龄相同再比较姓名
/*if (num==0){
boolean equals = this.name.equals(o.name);
if (equals){
num=0;
}
}
return num;*/
//代码优化
int num2=(num==0)?this.name.compareTo(o.name):num;
return num2;
4.3、TreeSet存储自定义对象并遍历练习2
- 按照姓名的长度进行排序
条件:主要是姓名的长度,然后是姓名,然后是年龄
public class MyTest5 {
public static void main(String[] args) {
TreeSet<Student> treeSet = new TreeSet<>();
treeSet.add(new Student("迈克尔.乔丹",40));
treeSet.add(new Student("科比.布莱恩特",38));
treeSet.add(new Student("詹姆斯",30));
treeSet.add(new Student("姚明",33));
treeSet.add(new Student("里奥梅西",23));
treeSet.add(new Student("王治郅",36));
treeSet.add(new Student("王仕鹏",30));
for (Student student : treeSet) {
System.out.println(student);
}
}
}
public int compareTo(Student o) {
//先比较姓名长度
int num = this.name.length() - o.name.length();
//姓名长度相同再比较年龄
int num1=(num==0)?this.age - o.age:num;
//年龄相同再比较姓名
int num2=(num1==0)?this.name.compareTo(o.name):num;
return num2;
4、比较器排序的原理
比较器排序:采用的是有参构造方法,构造方法中传入一个比较器(Comparetor 接口),需重写这个接口中的compare()方法,根据这个方法返回的 正 负 0来决定元素的排序。
- TreeSet(Comparator<? super E> comparator)
构造一个新的空 TreeSet,它根据指定比较器进行排序。
public class MyTest6 {
public static void main(String[] args) {
//根据年龄进行排序,年龄相同再比较姓名
TreeSet<Student> treeSet = new TreeSet<>(new Comparator<Student>() {
@Override
public int compare(Student s1, Student s2) {
int num = s1.getAge() - s2.getAge();
int num1=(num==0)?s1.getName().compareTo(s2.getName()):num;
return num1;
}
});
treeSet.add(new Student("乔丹",40));
treeSet.add(new Student("科比",38));
treeSet.add(new Student("詹姆斯",30));
treeSet.add(new Student("姚明",33));
treeSet.add(new Student("易建联",23));
treeSet.add(new Student("王治郅",36));
treeSet.add(new Student("王仕鹏",27));
treeSet.add(new Student("孙悦",29));
treeSet.add(new Student("姚明1",33));
treeSet.add(new Student("易建联1",23));
treeSet.add(new Student("王治郅1",36));
for (Student student : treeSet) {
System.out.println(student);
}
}
}
5、案例:产生10个1-20之间的随机数要求随机数不能重复
需求:编写一个程序,获取10个1至20的随机数,要求随机数不能重复。
并把最终的随机数输出到控制台。
选HashSet 可以不重复
选TreeSet 不重复还可以排序
分析:
a: 定义一个HashSet集合
b: 产生随机数,把随机数添加到集合中
c: 判断集合的长度,使用while循环实现
public class MyTest7 {
public static void main(String[] args) {
HashSet<Integer> hashSet = new HashSet<>();
Random random = new Random();
while (hashSet.size()<10){
hashSet.add(random.nextInt(20)+1);
}
System.out.println(hashSet);
}
}
6、键盘录入学生信息按照总分排序后输出在控制台
需求:键盘录入3个学生信息(姓名,语文成绩,数学成绩,英语成绩),按照总分从高到低输出到控制台。
步骤:
a: 自定义一个学生类
b: 创建一个TreeSet集合对象(使用比较器进行排序)
c: 键盘录入学生的数据,然后把学生的数据封装成一个学生对象,把学生对象添加到集合中
d: 遍历集合
public class MyTest8 {
public static void main(String[] args) {
TreeSet<Students> treeSet = new TreeSet<>(new Comparator<Students>() {
@Override
public int compare(Students s1, Students s2) {
double num = s1.getTotleScore()-s2.getTotleScore();
double num1=num==0?s1.getName().compareTo(s2.getName()):num;
return (int) num1;
}
});
for (int i = 1; i <= 3; i++) {
Scanner scanner = new Scanner(System.in);
System.out.println("请输入第"+i+"个学生姓名");
Students stu = new Students();
String name = scanner.nextLine();
stu.setName(name);
System.out.println("请输入第"+i+"个学生语文成绩");
double yw = scanner.nextDouble();
stu.setChineseScore(yw);
System.out.println("请输入第"+i+"个学生数学成绩");
double sx = scanner.nextDouble();
stu.setMathScore(sx);
System.out.println("请输入第"+i+"个学生英语成绩");
double yy = scanner.nextDouble();
stu.setEnglishScore(yy);
treeSet.add(stu);
}
System.out.println("序号\t姓名\t语文成绩\t数学成绩\t英语成绩\t总分");
int i =1;
for (Students students : treeSet) {
System.out.println((i++)+"\t"+students.getName()+"\t\t"+students.getChineseScore()+"\t\t"+students.getMathScore()+"\t\t"+students.getEnglishScore()+"\t\t"+students.getTotleScore());
}
}
}
public class Students {
private String name;
private double ChineseScore;
private double MathScore;
private double EnglishScore;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public double getChineseScore() {
return ChineseScore;
}
public void setChineseScore(double chineseScore) {
ChineseScore = chineseScore;
}
public double getMathScore() {
return MathScore;
}
public void setMathScore(double mathScore) {
MathScore = mathScore;
}
public double getEnglishScore() {
return EnglishScore;
}
public void setEnglishScore(double englishScore) {
EnglishScore = englishScore;
}
//获取总分
public double getTotleScore(){
return this.ChineseScore+MathScore+EnglishScore;
}
}