set接口:不允许数据重复,没有扩充方法,即Collection有什么方法,他就有什么方法,set集合的本质式map
常用子类:
HashSet:无序存储,通过hash放在不同的桶里了,按桶的顺序打印,底层使用hash表+红黑树,允许存放空值
再散列:当hash冲突发生时,再用hash算法rehash一次
拉链法:hash表,hash值相同时,通过链表连起来
TreeSet:有序存储,Comparable、Compartor接口,底层使用红黑树,不允许存放空值
Set接口:不允许数据重复(Set接口就是value值相同的map集合,先有Map才有Set)Set接口没有扩充方法
HashSet本质上就是HashMap,存放在了key值上,value值都一样,所以Set不允许重复(先有map再有set),value是一个Object对象
TreeSet本质上就是TreeMap,存放在了key值上,value值都一样
HashSet:无序存储-->HashMap(详见Map中)
1、底层使用hash表+红黑树
2、允许存放空值,无序存储
3、可以存放自定义类
TreeSet:有序存储--->TreeMap
1、底层使用红黑树
2、不允许存放空值,有序存储(comparable接口的序,String是ASCII码的序,Integet数值大小,也都实现了Comparable,)
3、自定义类要想保存到TreeSet中,要么实现Comparable,要么向TreeSet传入比较器(Compartor接口)
Comparable接口与Compartor接口区别:
答:1、在Java中,若想实现自定义类的比较,提供了以下两个接口
Java.lang.Comparable接口(内部比较器或排序接口):
若一个类实现了Comparable接口,就意味着该类支持排序,存放该类的Collections或数组,可以直接通过Collections.sort()或Array.sort进行排序
实现了Comparable接口的类可以直接存放在TreeSet或TreeMap中
2、Comparator(外部排序接口)
若要控制某个自定义类的顺序,而该类本身不支持排序,也就是说没有实现Comparable接口,我么可以建立一个该类的“比较器”来进行排序,比较器实现Compartor接口即可。
“比较器”:实现了Comparator接口的类作为比较器,通过该比较器来进行类的排序
3、实现了Comparator接口进行第三方排序-策略模式,此方法更加灵活,可以轻松改变策略进行第三方的排序算法
Comparable接口与Compartor接口关系:
答:
1、Java.lang.Comparable是排序接口,若一个类实现了Comparable接口,就意味着该类支持排序,是一个内部比较 器(自己和别人比)
2、Comparator是比较器接口,类本身不支持排序,专门有若干个第三方比较器(实现了Comparator接口的类)来进行类的排序,是一个外部比较器(策略模式)
public interface Comparable<T> package java.lang
public int compareTo(To);
返回值int的三种情况:
返回正数:表示当前对象大于目标对象
返回0:表示当前对象等于目标对象
返回负数:表示当前对象小于目标对象
TreeSet的一种构造方法:传入Comparator
public TreeSet(Comparator<?superE>comparator){
this(newTreeMap<>(comparator));
}
@FunctionalInterface//函数式编程接口
public interface Comparator<T> packagejava.util;
抽象方法:要强制实现
1、int compare(To1,To2);有两个参数,因为在类的外部进行比较,返回值与compareTo完全一致
返回值int的三种情况:
返回正数:表示当前对象大于目标对象
返回0:表示当前对象等于目标对象
返回负数:表示当前对象小于目标对象
2、booleanequals(Objectobj);
内部比较:两个对象直接比较,只用传一个参数,默认另一个式自己
外部比较:专门有一个比较的类,比较两个对象的大小,两个参数
public class ListPerson {
public static void main(String[] args) {
Set<Person> people = new TreeSet<Person>();
people.add(new Person("zhangsan",10));
people.add(new Person("gsan",10));
people.add(new Person("gsan",10));
System.out.println(people);
}
}
class Person implements Comparable<Person>{
private String name;
private int 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;
}
public Person(String name, int age) {
this.name = name;
this.age = age;
}
@Override
public String toString() {
return "Person{" +
"name='" + name + '\'' +
", age=" + age +
'}';
}
//自定义比较方法
@Override
public int compareTo(Person o) {
if(this.age>o.age){
return 1;
}else if(this.age<o.age){
return -1;
}else{
//String实现了Comparable接口
return this.name.compareTo(o.name);
}
}
}
实现Compartor接口,可以更方便的改变比较的方法
public class ListPerson {
public static void main(String[] args) {
Set<Person> people = new TreeSet<Person>(new AscAgeCompartor());
people.add(new Person("zhangsan",10));
people.add(new Person("gsan",10));
people.add(new Person("gsan",10));
System.out.println(people);
}
}
//升序。
class AscAgeCompartor implements Comparator<Person>{
@Override
public int compare(Person o1, Person o2) {
if(o1.getAge()>o2.getAge()){
return 1;
}else if(o1.getAge()<o2.getAge()){
return -1;
}else{
return o1.getName().compareTo(o2.getName());
}
}
}
//降序。
class DescAgeCompartor implements Comparator<Person>{
@Override
public int compare(Person o1, Person o2) {
return o2.getAge()-o1.getAge();
}
}
class Person{
private String name;
private int 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;
}
public Person(String name, int age) {
this.name = name;
this.age = age;
}
@Override
public String toString() {
return "Person{" +
"name='" + name + '\'' +
", age=" + age +
'}';
}
}
重复元素的判断:
TreeSet和TreeMap依靠Compartor或Comparable接口来区分重复元素。
自定义类想要保存在TreeSet或TreeMap中:
1、要么该类直接实现Comparable接口,覆写compareTo方法
2、要么实现一个比较器传入TreeSet或TreeMap来进行外部比较
而HashSet与HashMap并不依赖比较接口。
此时要想区分自定义的元素是否重估,需要同时覆写equals与hashcode方法。
Set<Person>people=newTreeSet<Person>(newAscAgeCompartor());
people.add(newPerson("zhangsan",10));
people.add(newPerson("gsan",10));
people.add(newPerson("gsan",10));
结果:[Person{name='gsan', age=10}, Person{name='zhangsan', age=10}]
Set<Person>people=newHashSet<Person>();
people.add(newPerson("zhangsan",10));
people.add(newPerson("gsan",10));
people.add(newPerson("gsan",10));
结果:[Person{name='gsan', age=10}, Person{name='zhangsan', age=10}, Person{name='gsan', age=10}]
因为没有覆写equals方法,比较地址,地址不一致,所以要覆写equals方法,来判定两个元素的内容是否相等。
覆写equals方法的原则:
1、自反性:对于任何非空引用值x,x.equals(x)都返回true
2、对称性:对于任何非空的x,y,当且仅当x.equals(y)返回true,y.equals(x)也返回true
3、传递性:对于任何非空的x,y,z,如果x.equals(y)返回true,y.equals(z)返回true,一定有x.equals(z)返回true
4、一致性:对于任何非空的x,y,若x与y中属性没有改变,则多次调用x.equals(y)始终返回true或false
5、非空性:对于任何非空引用x,x.equals(null)一定返回false
@Override
public boolean equals(Object o) {
//自反性
if (this == o){
return true;
}else if (o == null){//非空性
return false;
}else if (!(o instanceof Person)) {
return false;
}else{
Person person = (Person) o;
return age == person.age && this.name.equals(person.name);
}
}
覆写了equals并不能成功的去除重复元素,运行后还是得到[Person{name='gsan', age=10}, Person{name='zhangsan', age=10}, Person{name='gsan', age=10}],因为hash的存放原理:
1、先调用hashcode计算除对象hash码决定存放的数据桶,而后使用equals方法来比较元素是否相等,若相等不放置元素;若equals返回flase,则在相同桶之后,使用链表将若干元素链起来
2、Object类提供的hashCode方法默认使用对象的地址进行hash
所以equals方法和hashcode方法都要覆写
@Override
public int hashCode() {
return Objects.hash(age, name);
}
若两个对象equals方法返回true,他们的hashcode必然要保证相等
但是两个对象的hashcode相等,equals不一定相等
当且仅当equals与hashcode方法均返回true,才认为两个对象真正相等
public class ListPerson {
public static void main(String[] args) {
Set<Person> people = new HashSet<Person>();
people.add(new Person("zhangsan",10));
people.add(new Person("gsan",10));
people.add(new Person("gsan",10));
System.out.println(people);
}
}
class Person{
private String name;
private int 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;
}
public Person(String name, int age) {
this.name = name;
this.age = age;
}
@Override
public boolean equals(Object o) {
//自反性
if (this == o){
return true;
}else if (o == null){//非空性
return false;
}else if (!(o instanceof Person)) {
return false;
}else{
Person person = (Person) o;
return age == person.age && this.name.equals(person.name);
}
}
@Override
public int hashCode() {
return Objects.hash(age, name);
}
@Override
public String toString() {
return "Person{" +
"name='" + name + '\'' +
", age=" + age +
'}';
}
}
hash表:为甚麽要分桶来存放元素:优化查找次数
比较元素
比较接口:TreeSet、TreeMap
Comparable(内部排序):int compareTo(Object o)
Comparator(外部排序):int compare(Object o1,Object o2) 实现了Comparator接口的类叫比较器 实现外部排序更加灵活,想传升序传升序,想传降序传降序——>策略模式
比较方法:HashSet、HashMap
哈希表在存储元素时,先通过hashCode计算出对象的hash码,决定数据在那个桶中存放,所有hash码形同的元素通过equals再比较,如果不相等,链在链表的后端,如果相等则不存放
hashCode:求哈希码
equals:
equals相等的两个对象,hashCode一定要保证相等,hashCode相同的两个对象,equals不一定相等
equals的覆写原则
1、自反性
2、传递性
3、对称性
4、一致性
5、非空性