JAVAday17 Set集合
一 set集合概述
set集合之下有三个子类分别是
1.HashSet 底层数据结构是哈希表(数组+链表+二叉树)
元素无序(存取的顺序不一致),且元素唯一
2.LinkedHashSet 底层数据结构是哈希表和链表
元素有序,且唯一
3.TreeSet 底层数据结构是二叉树
元素唯一且可以对元素进行排序
二 HashSet, LinkedHashSet, TreeSet用法
2.1 HashSet如何保证元素的唯一性
当向 HashSet 集合中存入一个元素时,HashSet 会调用该对象的 hashCode() 方法来得到该对象的 hashCode 值,然后根据 hashCode 值决定该对象在 HashSet 中的存储位置。
HashSet 集合判断两个元素相等的标准:
两个对象通过 hashCode() 方法比较相等,并且两个对象的 equals() 方法返回值也相等。
结论:HashSet 保证元素唯一性是靠元素重写hashCode()和equals()方法来保证的,如果不重写则无法保证。
public class MyTest {
public static void main(String[] args) {
//HashSet 之所以能够保证元素的唯一性,是靠元素重写 equals()方法,来保证的,如果元素不重写该方法,则不能保证元素的唯一性
//合理重写hashCode 方法,是为了减少调用equals()的次数,也称之为减少碰撞
Student s1 = new Student("zhangsan", 19);
Student s2 = new Student("lisi", 19);
Student s3 = new Student("zhnagwu", 19);
Student s4 = new Student("liuqi", 20);
Student s5 = new Student("liuba", 23);
Student s6 = new Student("zhangsan", 19);
Student s7 = new Student("wnagwu", 19);
HashSet<Student> students = new HashSet<>();
students.add(s1);
students.add(s2);
students.add(s3);
students.add(s4);
students.add(s5);
students.add(s6);
students.add(s7);
//重写equals方法
public boolean equals(Object obj){
if(this == obj){
return ture;
}
if(!obj instanceof Student){
return false
}
Student stu = (Student)obj;
return this.name.equalsIgnoreCase(stu.name)&&this,age==stu.age;//完成去重
}
// 重写hascode方法
public int hashCode{
return this.name.hashCode()+this.age*10;
}
for (Student student : students) {
System.out.println(student);
}
2.2 LinkedHashSet的用法
链表保证元素有序,哈希表保证唯一
public class MyTest2 {
public static void main(String[] args) {
//LinkedHashSet 底层数据结构是链表和哈希表 元素有序且唯一 链表保证了有序,哈希表保证了唯一
//线程不安全效率高
LinkedHashSet<String> strings = new LinkedHashSet<>();
strings.add("aaa");
strings.add("bbb");
strings.add("ccc");
strings.add("ddd");
strings.add("eee");
strings.add("fff");
strings.add("aaa");
strings.add("bbb");
strings.add("ccc");
strings.add("ddd");
strings.add("eee");
strings.add("fff");
for (String string : strings) {
System.out.println(string);
}
ArrayList<String> strings1 = new ArrayList<>();
strings1.add("aaaaa");
strings1.add("bbbbb");
strings1.add("ccc");
strings1.add("dddd");
strings1.add("eeee");
strings1.add("aaaaa");
strings1.add("bbbbb");
strings1.add("ccc");
strings1.add("dddd");
strings1.add("eeee");
HashSet<String> strings2 = new HashSet<>(strings1);
System.out.println(strings2);
LinkedHashSet<String> strings3 = new LinkedHashSet<>(strings1);
System.out.println(strings3);
}
}
2.3 TreeSet集合的用法
TreeSet 排序方法有两种 1:自然排序 2:使用比较器排序
具体使用哪一种取决于构造方法
2.3.1 自然排序
二叉树的数据结构: 先存入一个树根 分两个叉,存储元素时 跟树根比较 小的放在左边 大的放在右边
如果相等就不存储,取的时候按照 左中右的顺序来取。
使用TreeSet集合进行自然排序时,要求元素必须实现Comparable接口,否则无法进行自然排序
保证元素唯一性是靠重写compare to方法的返回值来确定,如果返回0表示两个数相等,否则不重复存储。
public class MyTest {
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("李四大幅度发", 23));
treeSet.add(new Student("王五大幅度发", 20));
treeSet.add(new Student("周六的", 30));
treeSet.add(new Student("田七大幅度", 23));
treeSet.add(new Student("李白大幅度发", 33));
treeSet.add(new Student("刘星大幅度发", 63));
treeSet.add(new Student("夏雨", 78));
treeSet.add(new Student("张子豪", 53));
for (Student student : treeSet) {
System.out.println(student);
}
}
//实现comparable接口
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
//重写compareTo接口
public int compareTo(Student student) {
//根据学生的年龄大小来排序
//年龄一样,并不能说明是同一个对象,我们还得比较姓名是否一样
int num=this.age-student.age;
int num2=num==0?this.name.compareTo(student.name):num;
return -num2; //根据返回值的正 负 0 来决定元素的放置位置
}
}
2.3.2 比较器排序
采用有参构造,在创建TreeSet对象时Comparetor 比较器
public class MyTest {
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("李四大幅度发", 23));
treeSet.add(new Student("王五大幅度发", 20));
treeSet.add(new Student("周六的", 30));
treeSet.add(new Student("田七大幅度", 23));
treeSet.add(new Student("李白大幅度发", 33));
treeSet.add(new Student("刘星大幅度发", 63));
treeSet.add(new Student("夏雨", 78));
treeSet.add(new Student("张子豪", 53));
//可以写一下实际类或通过匿名内部类传入
TreeSet<Student>treeSet = new TreeSet<>(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 num2;
}
for (Student student : treeSet) {
System.out.println(student);
}
}
public class 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 +
'}';
}
}
//构造一个实际类
public class MyComparator implements Comparator<Student> {
@Override
public int compare(Student s1, Student s2) {
//按照年龄大小排序
int num = s1.getAge() - s2.getAge();
int num2=num==0?s1.getName().compareTo(s2.getName()):num;
return num2;
}
}