【Java】Set接口

Set接口(本质是一个Map接口)

set接口中没有扩充任何方法,set中有的方法都是Collection中有的方法。
set下的两个常用子类:

1.HashSet(无序存储),底层实现哈希表 + 红黑树(JDK1.8以后),JDK1.8以前只有哈希表。

public class SetTest {
    public static void main(String[] args) {
        Set<String> set = new HashSet<>();
        set.add("Hello");
        set.add("Java");
        set.add("SE");
        set.add("EE");
        set.add("Hello");//重复元素
        set.add(null);
        set.add(null);//重复元素
        System.out.println(set);
    }
}

结果:
在这里插入图片描述


2.TreeSet(如果没有自定义类则按照ASCII码,升序存储),不允许存放null,底层实现:红黑树

public class SetTest {
    public static void main(String[] args) {
        Set<String> set = new TreeSet<>();
        set.add("e");
        set.add("a");
        set.add("a");        
        set.add("c");
        set.add("A");
        System.out.println(set);
    }
}

在这里插入图片描述


1. TreeSet中自定义类如何保证元素升序与不重复?

首先看一下使用自定义类时,直接使用Java定义好的工具类什么都不做出现的结果:

class Person{
    private Integer age;
    private String name;

    public Person(Integer age, String name) {
        this.age = age;
        this.name = name;
    }

    public Integer getAge() {
        return age;
    }

    public String getName() {
        return name;
    }

    public void setAge(Integer age) {
        this.age = age;
    }

    public void setName(String name) {
        this.name = name;
    }

    @Override
    public String toString() {
        return "Person{" +
                "age=" + age +
                ", name='" + name + '\'' +
                '}';
    }
}

public class SetTest {
    public static void main(String[] args) {
        Set<Person> set = new TreeSet<>();
        set.add(new Person(20,"张三"));
        set.add(new Person(25,"李四"));
        System.out.println(set);
    }
}

结果:自定义的类如果不实现Comparable接口,或者comparator接口则不能比较。
非自定义的类可以比较是因为已经自己实现了Compara接口。
在这里插入图片描述

所以想让自定义类升序有两种方法:

1.java.lang.Comparable:内部排序接口
int compareTo(T o);
大于0:大于目标对象
等于0:等于目标对象
小于0:小于目标对象
类现了Comparable接口就表示此类具备可比较的性质,所以我们自定义的Person类想要能比较就需要实现Comparable接口。

class Person implements Comparable<Person>{
    private Integer age;
    private String name;

    public Person(Integer age, String name) {
        this.age = age;
        this.name = name;
    }

    public Integer getAge() {
        return age;
    }

    public String getName() {
        return name;
    }

    public void setAge(Integer age) {
        this.age = age;
    }

    public void setName(String name) {
        this.name = name;
    }

    @Override
    public String toString() {
        return "Person{" +
                "age=" + age +
                ", name='" + name + '\'' +
                '}';
    }

    @Override
    //首先比较年龄,如果年龄相同,则比较名字(名字按ASCII码排序)
    public int compareTo(Person o) {
        if (this.age < o.age)
            return -1;
        if (this.age > o.age)
            return 1;
        return this.name.compareTo(o.name);
    }
}

public class SetTest {
    public static void main(String[] args) {
        Set<Person> set = new TreeSet<>();
        set.add(new Person(25,"王五"));
        set.add(new Person(25,"李四"));
        set.add(new Person(20,"张三"));
        System.out.println(set);
    }
}

结果:
2.java.util.Comparator:外部排序接口(推荐使用)策略模式,不用修改源代码
类本身不具备可比较的特性,专门有一个类来比较自定义类的大小,此类专门用于比较两个Person的大小,以后如果需要更换比较策略,只需再写一个新的类就可以,不需要再次更改原代码。
int cpmpare(T o1,T o2);

package fwb.collection.Set;

import java.util.Comparator;
import java.util.Set;
import java.util.TreeSet;

class Person2 {
    private Integer age;
    private String name;

    public Person2(Integer age, String name) {
        this.age = age;
        this.name = name;
    }

    public Integer getAge() {
        return age;
    }

    public String getName() {
        return name;
    }

    public void setAge(Integer age) {
        this.age = age;
    }

    public void setName(String name) {
        this.name = name;
    }

    @Override
    public String toString() {
        return "Person2{" +
                "age=" + age +
                ", name='" + name + '\'' +
                '}';
    }
}

/*
此类专门用于比较两个Person的大小,以后如果需要更换比较策略,
只需再写一个新的类就可以,不需要再次更改原代码
 */
class PersonAgeSec implements Comparator<Person2>{

    @Override
    public int compare(Person2 o1, Person2 o2) {
        if(o1.getAge() < o2.getAge()){
            return -1;
        }
        if (o1.getAge() > o2.getAge()){
            return 1;
        }
        return 0;
    }
}

//若以后需要更改比较策略,只需要重新写一个类即可
class PersonAgeDesc implements Comparator<Person2>{

    @Override
    public int compare(Person2 o1, Person2 o2) {
        if(o1.getAge() < o2.getAge()){
            return 1;
        }
        if (o1.getAge() > o2.getAge()){
            return -1;
        }
        return 0;
    }
}


public class SetTest2 {
    public static void main(String[] args) {
        PersonAgeSec personAgeSec = new PersonAgeSec();
        //TreeSet有一个构造方法可以接收Comparator的子类
        Set<Person2> set = new TreeSet<>(personAgeSec);
        set.add(new Person2(25,"王五"));
        set.add(new Person2(25,"李四"));
        set.add(new Person2(20,"张三"));
        System.out.println(set);
    }
}

结果:
在这里插入图片描述
总结:
自定义类要想使用TreeSet:

  1. 自定义类自己实现了Comparable
  2. 要么是从外部传入一个该类的比较器对象(实现了Comparator接口)
    通过compare的返回值来判断是否相等,如果为0则重复

2. HashSet如何保证元素不重复?

HashSet使用equals与hashCode共同来判断元素是否重复
equals:判断两个对象属性是否相同。
hashCode:对象在内存中的地址根据Hash算法转为int。
所以如果自定义类需要覆写equals与hashCode方法:

package fwb.collection.Set;

import java.util.*;

/**
 * @program: collection
 * @description: HashSet
 * @author: fwb
 * @create: 2019-07-15 15:06
 **/

class Person3 {
    private Integer age;
    private String name;

    public Person3(Integer age, String name) {
        this.age = age;
        this.name = name;
    }
	
	//覆写equals方法
    @Override
    public boolean equals(Object obj){
        //当前对象 == obj(自己和自己比)
        if (this == obj){
            return true;
        }
        //如果obj不是当前类对象
        if (!(obj instanceof Person3)){
            return false;
        }
        //传进来的时此类对象,并且和自己不是同一个地址
        Person3 per = (Person3) obj;
        return this.age.equals(per.age)&& this.name.equals(per.name);
    }

	//覆写hashCode()方法
    @Override
    public int hashCode() {
        return Objects.hash(name,age);
    }

    public Integer getAge() {
        return age;
    }

    public String getName() {
        return name;
    }

    public void setAge(Integer age) {
        this.age = age;
    }

    public void setName(String name) {
        this.name = name;
    }

    @Override
    public String toString() {
        return "Person3{" +
                "age=" + age +
                ", name='" + name + '\'' +
                '}';
    }
}

public class SetTest3 {
    public static void main(String[] args) {
        Set<Person3> set = new HashSet<>();
        set.add(new Person3(25,"王五"));
        set.add(new Person3(25,"李四"));
        set.add(new Person3(20,"张三"));
        set.add(new Person3(20,"张三"));
        System.out.println(set);
    }
}

注:

  1. equlas相同,hashCode一定返回相同
  2. hashCode相同,equals不一定相同,因为hash算法不同。例:
//hash算法1:
//x = 10
hash(int i){
	return i + 1;
}
//hash算法2:
//x = 20
hash(int i){
	return i - 9;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

我顶得了

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值