Java——Set

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;

抽象方法:要强制实现

1int compare(To1,To2);有两个参数,因为在类的外部进行比较,返回值与compareTo完全一致

返回值int的三种情况:

返回正数:表示当前对象大于目标对象

返回0:表示当前对象等于目标对象

返回负数:表示当前对象小于目标对象

2booleanequals(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、非空性

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值