Java 集合——Set

本篇章中有些地方没有描述清楚,具体详细情况参考https://blog.csdn.net/b15735105314/article/details/117233527,因为set是基础map实现的。

一、Set概念

Set容器是一个不包含重复元素的Collection,并且最多包含一个null元素,它和List容器相反,Set容器不能保证其元素的顺序。

最常用的两个Set接口的实现类是HashSet和TreeSet.

 

二、HashSet

HashSet扩展AbstractSet并且实现Set接口

HashSet使用散列表(又称哈希表)进行存储

构造方法:

HashSet()默认构造方法
HashSet(Collection c)使用collection集合进行初始化
HashSet(int capacity)指定初始数组的大小
HashSet(int capacity, float fillRatio)指定初始数组的大小和扩容因子

 

HashSet没有定义任何超过它的父类和接口提供的其他方法

散列集合没有确保其元素的顺序,因为散列处理通常不参与排序,而且每次有新的元素加入的时候,顺序也会发生改变。

散列集合不保证它的元素的顺序。元素加入散列集合的顺序并不一定是他们被迭代读出的顺序。

自定义类作为HashSet的元素的时候,规定要重写类的hashCode() 和 equals() 两个方法。

HashSet通过元素的hashCode() 和 equals() 两个方法判断元素是否相同,如果元素的hashCode()方法相同且equals()也相等,那么HashMap就认为元素是相等的。
 

总结:HashSet的内部操作的底层数据是HashMap,只是我们操作的是HashMap的Key。

    @Getter
    @AllArgsConstructor
    static class Student implements Comparable<Student>{
        private String name;
        private Integer age;


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

        @Override
        public boolean equals(Object o) {
            if (this == o) return true;
            if (o == null || getClass() != o.getClass()) return false;
            Student student = (Student) o;
            return Objects.equals(name, student.name) &&
                    Objects.equals(age, student.age);
        }

        @Override
        public int hashCode() {
            return Objects.hash(name, age);
        }

        @Override
        public int compareTo(Student o) {
            return this.getName().compareTo(o.getName());
        }
    }

示例1:

    @Test
    public void hashSet(){
        Set<Student> set = new HashSet<>();
        Student stu1 = new Student("唐三", 25);
        Student stu2 = new Student("比比东", 50);
        Student stu3 = new Student("千道流", 130);
        Student stu4 = new Student("波塞西", 200);
        Student stu5 = new Student("唐三", 25);

        set.add(stu1);
        set.add(stu2);
        set.add(stu3);
        set.add(stu4);
        System.out.println(set.add(stu5));

        System.out.println(set);

    }

输出内容:
false
[Student{name='比比东', age=50}, Student{name='千道流', age=130}, Student{name='唐三', age=25}, Student{name='波塞西', age=200}]

上面实例中,姓名为“唐三”的student在第二次加入的时候是失败的,所以add方法返回false,HashMap在判定key相等的时候,会进行值的替换,HashSet在判定Key相等的时候,后面的值会被忽略掉。

三、TreeSet

TreeSet为使用树来进行存储的Set接口提供了一个工具,对象安升序存储,访问和检索很快

在存储了大量的需要进行快速检索的排序信息的情况下,TreeSet是一个很好的选择。

构造方法:

TreeSet()默认构造方法
TreeSet(Collection c)使用一个Collection对象进行初始化
TreeSet(Comparator comp)构造TreeSet,并传入比较对象Comparator
TreeSet(SortedSet ss) 

 

TreeSet使用Comparator的compare方法(或者元素所属类型实现Comparable接口的compareTo方法)判断元素是否相等,元素是否相等与hashCode() 和 equals() 两个方法没有任何关系,如果compare()或者compareTo()方法返回0就表示元素是相等的。

在创建TreeSet的时候,要么传入Comparator的实现类,要么元素的所属类实现Comparable的compareTo方法,二者必须选择一个;且如果二者同时满足,会选择传入的Comparator的实现类进行元素的比较。

Comparator#compare:

Comparable#compareTo:
 

总结:TreeSet的内部操作的底层数据是TreeMap,只是我们操作的是TreeMap的key。

示例2:

    @Test
    public void treeSet(){
        Set<Student> set = new TreeSet<>();
        Student stu1 = new Student("tangsan", 25);
        Student stu2 = new Student("bibidong", 50);
        Student stu3 = new Student("qiandaoliu", 130);
        Student stu4 = new Student("bosaixi", 200);
        Student stu5 = new Student("tangsan", 26);

        set.add(stu1);
        set.add(stu2);
        set.add(stu3);
        set.add(stu4);
        System.out.println(set.add(stu5));

        System.out.println(set);

    }

输出内容:
false
[Student{name='bibidong', age=50}, Student{name='bosaixi', age=200}, Student{name='qiandaoliu', age=130}, Student{name='tangsan', age=25}]

 Student实现comparable接口的compareTo时,使用的时用户姓名座对比,所以两个“tangsan”student,只有第一次的被存储。第二次存储“tangsan”时返回false。

示例3:

    @Test
    public void treeSet(){
        Set<Student> set = new TreeSet<>(new Comparator<Student>() {
            @Override
            public int compare(Student s1, Student s2) {
                if(s1.getName().compareTo(s2.getName()) > 0){
                    return 1;
                } else if (s1.getName().compareTo(s2.getName()) < 0){
                    return -1;
                } else {
                    return s1.getAge().compareTo(s2.getAge());
                }
            }
        });
        Student stu1 = new Student("tangsan", 25);
        Student stu2 = new Student("bibidong", 50);
        Student stu3 = new Student("qiandaoliu", 130);
        Student stu4 = new Student("bosaixi", 200);
        Student stu5 = new Student("tangsan", 26);

        set.add(stu1);
        set.add(stu2);
        set.add(stu3);
        set.add(stu4);
        System.out.println(set.add(stu5));

        System.out.println(set);

    }

输出内容:
[Student{name='bibidong', age=50}, Student{name='bosaixi', age=200}, Student{name='qiandaoliu', age=130}, Student{name='tangsan', age=25}, Student{name='tangsan', age=26}]

上面示例中传入了Comparator对象,如果传入Comparator对象,那么就不会再使用自然顺序进行排序,Comparator在实现的时候,先使用姓名进行对比,在姓名相同的时候,使用年龄进行对比。所以上面的示例中两个“tangsan”都成功存储,而且25岁的在前,26岁的在后。

四、LinkedHashSet

LinkedHashSet是基于HashSet实现的,所以特性参考HashSet就行,唯一的区别就是LinkedHashSet可以保证输入顺序和输出顺序保持一致。

示例:

    @Test
    public void linkedHashSet(){
        Set<Student> set = new LinkedHashSet<>();
        Student stu1 = new Student("唐三", 25);
        Student stu2 = new Student("比比东", 50);
        Student stu3 = new Student("千道流", 130);
        Student stu4 = new Student("波塞西", 200);
        Student stu5 = new Student("唐三", 25);

        set.add(stu1);
        set.add(stu2);
        set.add(stu3);
        set.add(stu4);
        System.out.println(set.add(stu5));

        System.out.println(set);

    }

输出内容:
false
[Student{name='唐三', age=25}, Student{name='比比东', age=50}, Student{name='千道流', age=130}, Student{name='波塞西', age=200}]

 

评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值