Set接口
特点:
1 无序
2 继承自Collection的接口,无重复元素
3 可以存null值,但只有一个
4 set的实现类大多数都是基于Map实现的
5 成员方法全部来自Object类和Collection类
HashSet类
实现Set接口,由哈希表(HashMap)支持。
不保证Set的迭代顺序,元素允许为null.
特点
底层数据结构是哈希表(JDK1.8数据结构为数组 + 链表 + 红黑树)
去除重复元素依赖hashcode和equals方法
当hashcode相等时,比较元素的equals方法:
相等: 不存储到哈希表结构中,去除重复元素
不相等:挂在链表后面
hashcode不相等,存入元素对应hashcode值的数组上
当链表上的节点数大于8时,链表转换为红黑树,当小于6时,变回链表;
Set单列集合注意点
1自定义类idea可直接生成重写hashcode和equals方法,不需手写。
2equals方法效率太低了,他是对该类对象的所有属性慢慢一 一比较才能得出两个对象是否相等,这个效率太低了,不能处理数据量大的情况,那么jdk为我们提供了另一种比较方法,hashCode方法.
3 当两个对象的哈希值不同,那么这两个对象肯定不同,这个很容易明白,可当两个对象的哈希值相同时,则不能判定两个对象一定相同,因为哈希值运算操作中不能保证所有运算的哈希值不同,所以当两个对象的哈希值相同时,还要调用equals方法比较,挨个比较所有属性值才能确定两个对象是都否等。
4重写equals方法,可以不用重写hashCode方法,但这样效率很低,不能处理数据量大的情况,所以一般重写了equals方法我们都会重写hashCode方法来提高效率(当然哈希值很难有重复的,只要不刻意一般都不会碰到哈希值相同的情况)。这样就很好的保证了Set集合的不可重复。
通过源码分析+数据结构原理图我们可以得出如下结论:
1.首先通过元素本身计算哈希索引,将这个值作为索引存储到哈希表结构中,由于这个值和对象本身的hashode有关,所以我们存储的元素需要重写hashCode方法,重写hashCode方法是为了提高哈希表的性能。
2.如果两个对象的hash值不同,那么完全能够说明两个对象不是同一个对象,那么我们就将该元素存储到哈希表结构中
3.如果两个对象的hash值相同(哈希冲突),那么这个时候不能够完全说明两个元素相等,还需要比较两个对象的equals方法,所以我们还需要重写equals方法。
4.当两个对象的equals方法不同,那么我们就可以以链表的形式把该元素存储到哈希表结构中同一个索引的位置。
5.两个对象的equals方法相同,那么我们可以当成重复元素去除,不存储到哈希表结构中。
6.当链表长度大于 TREEIFY_THRESHOLD(默认为8) 的时候,将链表转换为红黑树,以提高查找的性能,当红黑树节点数量小于 UNTREEIFY_THRESHOLD(默认为6)又将红黑树转回为链表以达到性能均衡。
TreeSet类
概述
基于 TreeMap 的 NavigableSet 实现。使用元素的自然顺序(Comparable)对元素进行排序,或者根据创建 set 时提供的 (Comparotor)进行排序,具体取决于使用的构造方法。
特点
底层数据结构是红黑树
TreeSet依赖二叉树取中序遍历元素的特点能够去除重复元素
TreeSet的两种排序方式是如何实现的
a:自然排序:使用TreeSet的无参构造方法
b:比较器排序:使用TreeSet的带Comparotor接口参数的构造方法
TreeSet的主要作用是进行排序,由于底层原理使用到了树结构,其本身在存储时就对元素做了一些排序。当然排序的规则要规定,可以使泛型类型实现Comparable接口或者Comparator接口。如果是Comparable接口,则compareTo方法返回的是固定值0的话,集合只有一个元素,是固定值-1的话集合倒序存储,是固定值1的话集合按序存储,当然按元素比较结果排序时,则是按红黑树结构排序。
public static void main(String[] args) {
//比较器排序
TreeSet<Student> s = new TreeSet<>(new Comparator<Student>() {
@Override
public int compare(Student s1, Student s2) {
Collator c = Collator.getInstance(Locale.CHINA);
int cmp = s1.getAge().compareTo(s2.getAge());
int cmp2 = cmp == 0 ? s1.getScore().compareTo(s2.getScore()) : cmp;
int cmp3 = cmp2 == 0 ? s1.getChinese().compareTo(s2.getChinese()) : cmp2;
int cmp4 = cmp3 == 0 ? s1.getMath().compareTo(s2.getMath()) : cmp3;
int cmp5 = cmp4 == 0 ? s1.getEnglish().compareTo(s2.getEnglish()) : cmp4;
int cmp6 = cmp5 == 0 ? c.compare(s1.getName(), s2.getName()) : cmp5;
return cmp6;
}
});
s.add(new Student("zs",23,74.4,76.6,79.7,73.7));
s.add(new Student("ls",24,200.7,73.7,72.4,74.4));
s.add(new Student("ww",25,203.3,72.7,75.3,73.5));
s.add(new Student("ww",25,203.8,72.9,75.2,73.0));
s.add(new Student("ls",26,209.4,77.6,72.8,79.9));
for (Student stu : s) {
System.out.println(stu);
}
}
}
class Student implements Comparable<Student>{
private String name;
private Integer age;
private Double score;
private Double chinese;
private Double math;
private Double english;
public Student() {
}
public void setName(String name) {
this.name = name;
}
public void setAge(Integer age) {
this.age = age;
}
public void setScore(Double score) {
this.score = score;
}
public void setChinese(Double chinese) {
this.chinese = chinese;
}
public void setMath(Double math) {
this.math = math;
}
public void setEnglish(Double english) {
this.english = english;
}
public String getName() {
return name;
}
public Integer getAge() {
return age;
}
public Double getScore() {
return score;
}
public Double getChinese() {
return chinese;
}
public Double getMath() {
return math;
}
public Double getEnglish() {
return english;
}
public Student(String name, int age, Double score, Double chinese, Double math, Double english) {
this.name = name;
this.age = age;
this.score = score;
this.chinese = chinese;
this.math = math;
this.english = english;
}
@Override
public String toString() {
return "Student{" +
"name='" + name + '\'' +
", age=" + age +
", score=" + score +
", chinese=" + chinese +
", math=" + math +
", english=" + english +
'}';
}
@Override
public int compareTo(Student s) {
Collator c = Collator.getInstance(Locale.CHINA);
int num1=this.getAge().compareTo(s.getAge());
int num2=(num1==0)?this.getScore().compareTo(s.getScore()):num1;
int num3=(num2==0)?this.getChinese().compareTo(s.getChinese()):num2;
int num4=(num3==0)?this.getMath().compareTo(s.getMath()):num3;
int num5=(num4==0)?this.getEnglish().compareTo(s.getEnglish()):num4;
int num6=(num5==0)?c.compare(s.getName(),this.getName()):num5;
return num6;
}
LinkedHashSet类
继承HashSet,但底层是通过LinkedHashMap来实现的,在HashSet的基础上并没有什么延申,还是HashSet的一些操作,但底层实现原理变了,
底层是双向链表,这就使他变得有序,怎么存入就是怎么取出
特点
底层数据结构是链表 + 哈希表
链表保证元素有序
哈希表保证元素唯一