1.Set接口与List接口一样,同样继承于Collection接口,与Collection接口中的方法基本一致,只是比Collection接口更加严格,与List接口不同的是,Set接口中的元素无序,并且都会以某种规则保证存入的元素不出现重复。
1.HashSet集合是Set接口的一个实现类,底层数据结构是哈希表,所存储的元素是不可重复的,并且元素都是无序的(即存取顺序不一致)。HashSet的底层其实是一个HashMap支持。HashMap后面会进行学习
2.Set集合存储的代码实现:
import java.util.HashSet;
public class HashSetDemo01 {
public static void main(String[] args) {
HashSet<String> hashSet = new HashSet<>();
hashSet.add("abc");
hashSet.add("234");
hashSet.add("123");
hashSet.add("456");
//存取顺序不同
for (String s : hashSet) {`在这里插入代码片`
System.out.print(s+" ");//123 abc 234 456
}
}
}
3.存取顺序不同的原理:
HahSet集合的底层是哈希表,哈希表在JDK1.8之前,哈希表底层采用数组+链表实现,即使用链表处理冲突,同一hash值的链表都存储在一个链表里。但是当位于一个桶中的元素较多,即hash值相等的元素较多时,通过key值依次查找的效率较低。而JDK1.8中,哈希表存储采用数组+链表+红黑树实现,当链表长度超过阈值(8)时,将链表转换为红黑树,这样大大减少了查找时间。
JDK1.8引入红黑树大程度优化了HashMap的性能,那么对于我们来讲保证HashSet集合元素的唯一,
其实就是根据对象的hashCode和equals方法来决定的。如果我们往集合中存放自定义的对象,那么保证其唯一,
就必须复写hashCode和equals方法建立属于当前对象的比较方式。
4.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;
}
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 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);
}
@Override
public String toString() {
return "Student{" + "name='" + name + '\'' + ", age=" + age + '}';
}
}
测试类:
import java.util.HashSet;
public class HashSetTest01 {
public static void main(String[] args) {
/*
Student类重写了equals方法和hashcode方法
如果元素不重写这两个方法,则无法保证元素的唯一性。合理的重写hashCode方法,是为了减少碰撞(减少调用equals方法的次数)
*/
Student s1 = new Student("张三", 23);
System.out.println(s1.hashCode());//24022543
Student s2 = new Student("张三", 23);
System.out.println(s2.hashCode());//24022543
Student s3 = new Student("李四", 24);
Student s4 = new Student("王五", 25);
Student s5 = new Student("赵六", 26);
Student s6 = new Student("赵六", 27);
//使用HashSet集合存储Student对象
HashSet<Student> hashSet = new HashSet<>();
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);
}
}
}
HashSet保证元素唯一,可是元素存放进去是没有顺序的,在HashSet下面有一个子类LinkedHashSet类,它是链表和哈希表组合的一个数据存储结构。可以保证元素唯一且存储有序。链表保证有序 哈希表保证元素唯一
import java.util.LinkedHashSet;
public class LinkedHashSetTest01 {
public static void main(String[] args) {
LinkedHashSet<String> linkedHashSet = new LinkedHashSet<>();
linkedHashSet.add("aaa");
linkedHashSet.add("bbb");
linkedHashSet.add("ccc");
linkedHashSet.add("ddd");
for (String s : linkedHashSet) {
System.out.println(s);
}
}
}
TreeSet集合的特点:底层是一个二叉树,保证数据元素唯一在,并且可对元素进行排序
TreeSet集合有两种排序方式:(1)自然排序 (2)使用比较器排序
TreeSet集合可以存储Integer类型的数据进行遍历
import java.util.TreeSet;
public class TreeSetDemo01{
public static void main(String[] args) {
TreeSet<Integer> treeSet = new TreeSet<>();
treeSet.add(10);
treeSet.add(30);
treeSet.add(5);
treeSet.add(20);
for (Integer integer : treeSet) {
System.out.print(integer+" ");//5 10 20 30
}
}
}
当TreeSet集合存储自定义类型时,使用TreeSet集合对其进行排序
如果里面存储自定义类型,当存入自定义的引用类型的时候就必须考虑到元素不可重复的这个特性,不然会报ClassCastException这种错误,所以要对自定义类型进行处理,必须实现 Comparable与Compared接口,排序方式有自然排序和比较器排序。
方式1:在存入集合的元素所对应的类中,实现Comparable接口,重写里面的public int compareTo(自定义类名 o);
自定义Student类:
//自定义Student类实现Comparable接口,泛型使用自定义类名
public class Student implements Comparable<Student>{
private String name;
private int age;
public Student(String name, int age) {
this.name = name;
this.age = age;
}
@Override
public String toString() {
return "Student{" +
"name='" + name + '\'' +
", 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 compareTo(Student o) {//this.name.length()-o.name.length():num1;
//先比较姓名的长度
int num1=this.name.length()-o.name.length();
//当年龄相同时,比较姓名
int num2=num1==0?this.name.compareTo(o.name):num1;
//最后比较年龄
return num2==0?this.age-o.age:num2;
}
}
测试类:
public class MyTestDemo {
public static void main(String[] args) {
TreeSet<Student> set = new TreeSet();
set.add(new Student("张三1",20));
set.add(new Student("张三2",23));
set.add(new Student("张三3",20));
set.add(new Student("张三44444",25));
set.add(new Student("张三44444",28));
set.add(new Student("张三555555",28));
set.add(new Student("张三5",28));
set.add(new Student("张三6we",28));
set.add(new Student("张三7",29));
set.add(new Student("张三7",29));
set.add(new Student("张三8",29));
for (Student student : set) {
System.out.println(student);
}
}
}
测试结果:
方式2:当无法改变存入集合的元素所对应的类的源码时,可以在创建TreeSet集合的时候,通过TreeSet的构造方法传入一个匿名内部类的比较器
import java.util.Comparator;
import java.util.TreeSet;
public class sdsdsds {
public static void main(String[] args) {
//参数传入一个匿名内部类,里面实现compare方法
TreeSet<Student> set = new TreeSet<>(new Comparator<Student>() {
@Override
public int compare(Student o1, Student o2) {
//先比较姓名的长度
int num1=o1.getName().length()-o2.getName().length();
//当年龄相同时,比较姓名
int num2=num1==0?o1.getName().compareTo(o2.getName()):num1;
//最后比较年龄
return num2==0?o1.getAge()-o2.getAge():num2;
}
});
set.add(new Student("张三1",20));
set.add(new Student("张三2",23));
set.add(new Student("张三3",20));
set.add(new Student("张三44444",25));
set.add(new Student("张三44444",28));
set.add(new Student("张三555555",28));
set.add(new Student("张三5",28));
set.add(new Student("张三6we",28));
set.add(new Student("张三7",29));
set.add(new Student("张三7",29));
set.add(new Student("张三8",29));
for (Student student : set) {
System.out.println(student);
}
}
}
运行结果和方法1相同
键盘录入3个学生信息(姓名,语文成绩,数学成绩,英语成绩),按照总分从高到低输出到控制台。
//创建一个学生类,录入学生的各个信息
public class Students {
private String name;
private Double ChineseGrade;
private Double MathGrade;
private Double EngLishGrade;
public Students() {
}
public Students(String name, Double chineseGrade, Double mathGrade, Double engLishGrade) {
this.name = name;
ChineseGrade = chineseGrade;
MathGrade = mathGrade;
EngLishGrade = engLishGrade;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public Double getChineseGrade() {
return ChineseGrade;
}
public void setChineseGrade(Double chineseGrade) {
ChineseGrade = chineseGrade;
}
public Double getMathGrade() {
return MathGrade;
}
public void setMathGrade(Double mathGrade) {
MathGrade = mathGrade;
}
public Double getEngLishGrade() {
return EngLishGrade;
}
public void setEngLishGrade(Double engLishGrade) {
EngLishGrade = engLishGrade;
}
//获取总分数的方法
public Double getAboveGrade(){
return ChineseGrade+MathGrade+EngLishGrade;
}
}
测试类
import java.util.Comparator;
import java.util.Scanner;
import java.util.TreeSet;
public class MyTest03 {
public static void main(String[] args) {
//键盘录入3个学生信息(姓名,语文成绩,数学成绩,英语成绩),按照总分从高到低输出到控制台。
//创建一个TreeSet集合 参数传递Comparator接口的一个匿名内部类,实现比较方法
TreeSet<Students> set = new TreeSet<>(new Comparator<Students>() {
@Override
public int compare(Students o1, Students o2) {
//按分数从大到小的是顺序
int num1 = -(int)(o1.getAboveGrade() - o2.getAboveGrade());
//当分数相同的,比较姓名
return num1==0?o1.getName().compareTo(o2.getName()):num1;
}
});
//循环3次,录入学生姓名以及成绩
for (int i = 1; i <= 3; i++) {
Scanner scanner = new Scanner(System.in);
Students student = new Students();
System.out.println("请输入第"+i+"个学生的姓名");
String name = scanner.nextLine();
student.setName(name);
System.out.println("请输入第"+i+"个学生的语文成绩");
double Chinese = scanner.nextDouble();
student.setChineseGrade(Chinese);
System.out.println("请输入第"+i+"个学生的数学成绩");
double math = scanner.nextDouble();
student.setMathGrade(math);
System.out.println("请输入第"+i+"个学生的英语成绩");
double english = scanner.nextDouble();
student.setEngLishGrade(english);
//添加学生信息
set.add(student);
}
System.out.println("姓名 语文 数学 英语");
for (Students students : set) {
System.out.println(students.getName()+students.getChineseGrade()+students.getMathGrade()+students.getEngLishGrade()+students.getAboveGrade());
}
}
}
练习:获取10个1至20的随机数,要求随机数不能重复。
import java.util.Random;
import java.util.TreeSet;
public class MyTest02 {
public static void main(String[] args) {
/* 要求随机数不重复,可以选择两种集合。选HashSet 可以保证元素不重复 选TreeSet 保证不重复并且还可以对元素排序
*/
TreeSet<Integer> set = new TreeSet<>();
while (set.size()<10){
Random random = new Random();
int i = random.nextInt(20) + 1;
set.add(i);
}
System.out.println(set);//[5, 8, 9, 10, 11, 12, 14, 15, 18, 20]
}
}
import java.util.Arrays;
import java.util.Comparator;
public class random {
public static void main(String[] args) {
Integer[] arr={20,60,45,44,78,12};
//使用Arrays工具类对Integer进行排序,默认从小到大排序
Arrays.sort(arr);
System.out.println(Arrays.toString(arr));//[12, 20, 44, 45, 60, 78]
//使用Arrays工具类对Integer进行排序,参数传递Comparator的一个匿名内部类
//根据指定比较器产生的顺序对指定对象数组进行排序。
Arrays.sort(arr, new Comparator<Integer>() {
@Override
public int compare(Integer o1, Integer o2) {
return -(o1-o2);
}
});
System.out.println(Arrays.toString(arr));//[78, 60, 45, 44, 20, 12]
}
}