Java笔记:collection集合-set类集合

之前说过,collection集合下分为两类集合,一类是list类,一类是set类集合,两种集合各有各自的特点:

list集合:有序,可重复,有索引

set集合:无序,不可重复,无索引

我们知道list集合中的Arrylist集合底层就是用数组实现的,固然有索引,那么为什么set集合就没有索引呢?

让我们先来了解set集合的底层原理:

哈希表 = 数据+链表(JDK8之前)

哈希表 = 数组+链表+红黑树(JDK8之后)

注意:这里的数组和Arrylist底层一样,都是动态数组,当有75%的位置存入数据时,数组会自动扩容。

这里引入了哈希表的概念,让我们先来了解一下哈希值:哈希值是一个十进制的整数,有系统随机给出(就是对象的地址值,是一个逻辑地址,是模拟出来的地址,不是数据实际存储的物理地址)。

无序、无索引的原因:当集合中存入数据时,底层会对数据的哈希值进行求余,求出的值即为数组的下标,然后底层将数据存在数组的这个下标处。因为每个数据的哈希值不同,所以求出的数组下标是无序杂乱的,故无索引。

不重复的原因:当数据的哈希值重复(哈希碰撞)时,底层将会以链表的形式将新传入的数据挂在老数据下方,同时底层设置了equals方法,当判断存入的数据和之前某个存入的数据相同时,不会存入,故不重复。

那么,这里肯定会有大聪明担心了:如果链表的长度过长,查找数据的效率还是会变慢呀?

对,所以设计者也想到了,在JDK8之后,如果数组场长度大于等于64,且某个链表长度大于8时,底层自动将链表转换为红黑树,方便查找。

所以:set集合是一种增删改查性能都较好的数据结构。

了解完了set集合的底层原理,我们来了解一下常见的三种set集合:

HashSet、LinkedHashSet、TreeSet:

    public static void main(String[] args) {
        //1.创建一个set集合的对象
        Set<Integer> set = new HashSet<>();//无序不重复无索引
        set.add(666);
        set.add(555);
        set.add(555);
        set.add(777);
        set.add(888);
        set.add(888);
        set.add(666);
        System.out.println(set);
        Set<Integer> set2 = new LinkedHashSet<>();//有序不重复无索引
        set2.add(666);
        set2.add(555);
        set2.add(777);
        set2.add(888);
        set2.add(888);
        set2.add(666);
        System.out.println(set2);
        Set<Integer> set3 = new TreeSet<>();//有序不重复无索引,默认升序排序
        set3.add(666);
        set3.add(555);
        set3.add(777);
        set3.add(888);
        set3.add(888);
        set3.add(666);
        System.out.println(set3);
    }

 欸,这个TreeSet看着有点熟悉哦,没错,只有在基本数据类型时他才能默认排序,当出现定义数据类型的时候,我们需要在类中重写CompareTo方法来规定排序规则

例:我们这里定义一个学生类

public class Student implements Comparable<Student> {
    @Override
    public int compareTo(Student o) {
        //左>右   1
        //左=右   0
        //左<右  -1
        return this.age - o.age;
    }

    public Student(String name, int age, double height) {
        this.name = name;
        this.age = age;
        this.height = height;
    }

    public Student() {
    }

    private String name;
    private int age;
    private double height;

    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 double getHeight() {
        return height;
    }

    public void setHeight(double height) {
        this.height = height;
    }

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

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

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

重写了compareTo方法之后,就可以进行比较辣!

那么,除此之外,还有第二种比较方法,就是调用treeSet集合自带的有参构造器,可以设置Comparator对象。

 public static void main(String[] args) {
        Set<Integer> tree =  new TreeSet<>();
       

    //比较定义类方式一:让定义类实现comparable接口,重写里面的compareTo方法
    //方式二:通过treeSet集合自带的有参构造器,可以设置Comparator对象(比较器对象,属于指定比较规则)
    Set<Student> students = new TreeSet<>(new Comparator<Student>() {
        @Override
        public int compare(Student o1, Student o2) {
            return Double.compare(o1.getHeight(), o2.getHeight());
        }
    });

    //如果同时重写方法和调用treeSet比较器对象,则treeSet会优先执行比较器对象,
        // 不会执行重写的compareTo方法


    students.add(new Student("张三",23,161.1));
    students.add(new Student("李四",21,159.8));
    students.add(new Student("王五",25,168.8));
    students.add(new Student("赵六",19,171.9));
    students.add(new Student("王五",25,168.8));
    System.out.println(students);
    //这里执行时发现王五丢了,原因是王五中的比较数据都相等,treeSet不重复,直接抹掉一个
    }

欸,靓仔你应该发现了吧,这是啥,匿名内部类啊!再点进去看源码,是函数式接口,好,直接搬出我们的lambda表达式!

 public static void main(String[] args) {
        Set<Integer> tree =  new TreeSet<>();
        tree.add(1);
        tree.add(4);
        tree.add(5);
        tree.add(3);
        System.out.println(tree);//有序,不重复,无索引  底层:红黑树
//注意:treeSet中对于基本类型的Interage、Double,按默认值本身大小可以做到升序排序
//对于字符串类型:默认按照首字母的编号升序排序
// 但是对于自定义的类型如Student类是不能直接默认进行排序的


    //比较定义类方式一:让定义类实现comparable接口,重写里面的compareTo方法
    //方式二:通过treeSet集合自带的有参构造器,可以设置Comparator对象(比较器对象,属于指定比较规则)
//    Set<Student> students = new TreeSet<>(new Comparator<Student>() {
//        @Override
//        public int compare(Student o1, Student o2) {
//            return Double.compare(o1.getHeight(), o2.getHeight());
//        }
//    });


//化简后:
    Set<Student> students = new TreeSet<>( (o1,  o2)-> Double.compare(o1.getHeight(), o2.getHeight()));


    students.add(new Student("张三",23,161.1));
    students.add(new Student("李四",21,159.8));
    students.add(new Student("王五",25,168.8));
    students.add(new Student("赵六",19,171.9));
    students.add(new Student("王五",25,168.8));
    System.out.println(students);
    //这里执行时发现王五丢了,原因是王五中的比较数据都相等,treeSet不重复,直接抹掉一个
    }

 好了,到这里差不多就是set集合的所有内容了!

但是,我们没有一刻的停留,接下来赶到战场的是——Map集合!

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值