Java基础之HashSet和TreeSet

单列集合

Collection是单列集合的根接口

Collection有两个重要的子接口,分别是List和Set

Set集合的特点

无序:元素存入和取出的顺序无法保证一致性

不重复:重复的元素不会被存入

无索引:集合中没有索引,无法通过索引操作元素
(因为无索引,只能是迭代器,增强for遍历)

HashSet

HashSet集合会默认根据对象的地址去除重复

如果需要根据属性去重,需要重写元素的hashCode和equals方法

HashSet底层结构

HashSet的底层使用了哈希表存储元素

哈希表

哈希表也叫散列表,常用数组+链表实现,元素根据哈希值计算在哈希表(数组)中的存储位置

如果元素出现哈希冲突(在哈希表中存储位置相同),则采用链表挂载元素。

哈希值

哈希值代表对象的某种特征,使用int整数表示。

Object类中提供了hashCode()方法,可以根据对象的内存地址计算出哈希值。

Java类可以重写hashCode()方法,自定义哈希值的计算规则。

元素添加流程

1.先调用元素的hashCode()方法,计算出在数组中的位置

2.如果存储位置为空,直接存入元素

3.如果存储位置不为空,调用equals方法进行比较和该位置的所有元素逐一比较,如果为true,表示要存入的元素已经存在,元素将不再重复添加。

扩容机制

优化方式:将数组变长

哈希数组的默认初始长度为16

当存储元素个数超过阈值时,进行扩容(阈值 = 数组长度 * 扩容因子)

扩容因子默认是0.75,超过阈值进行扩容,每次扩容为原先的2倍

扩容是为了降低哈希冲突,以便缩短链表的长度,提高元素比较和查找的性能。

树化

优化方式:链表进行树化

从JDK8开始,哈希表优化为数组+链表+红黑树实现。

当某个链表元素超过8个,并且数组长度>=64,链表会转为红黑树进行存储。

树化的目的和扩容一样,也是为了提高元素比较和查找的能力。

TreeSet集合特点

TreeSet是Set接口下的集合

元素无索引,不重复

TreeSet集合会自动对元素进行排序

排序规则

元素为数字时,默认按照升序排序。

元素为字符串时,按照字符的编码值升序排序。

如果元素为自定义类型,需要指定排序规则。

public static void main(String[] args) {
        //升序
        TreeSet<Integer> set = new TreeSet<>();
        set.add(1);
        set.add(7);
        set.add(5);
        set.add(2);
        set.add(9);

        //按字符的编码值排序,若首字母相同,则比较第二位,依次类推
        TreeSet<String> set1 = new TreeSet<>();
        set1.add("abc");
        set1.add("bbb");
        set1.add("aaa");
        set1.add("好");
        set1.add("大家");

        //字符转int查看编码值
        System.out.println((int)'黑');

        for (Integer i :
                set) {
            System.out.println(i);
        }

        for (String s :
                set1) {
            System.out.println(s);
        }
    }

在这里插入图片描述

Comparable

存自定义数据

public class Student {
    private String name;
    private int age;

    public Student() {
    }

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

    /**
     * 获取
     * @return name
     */
    public String getName() {
        return name;
    }

    /**
     * 设置
     * @param name
     */
    public void setName(String name) {
        this.name = name;
    }

    /**
     * 获取
     * @return age
     */
    public int getAge() {
        return age;
    }

    /**
     * 设置
     * @param age
     */
    public void setAge(int age) {
        this.age = age;
    }

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


}

自定义的数据不具备比大小的能力,必须去实现 Comparable

public class Student implements Comparable<Student>{
    private String name;
    private int age;

    @Override
    public int compareTo(Student o) {
        //return this.age - o.age;//升序
        return o.age - this.age;//降序
    }

    public Student() {
    }

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

    /**
     * 获取
     * @return name
     */
    public String getName() {
        return name;
    }

    /**
     * 设置
     * @param name
     */
    public void setName(String name) {
        this.name = name;
    }

    /**
     * 获取
     * @return age
     */
    public int getAge() {
        return age;
    }

    /**
     * 设置
     * @param age
     */
    public void setAge(int age) {
        this.age = age;
    }

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


}
public static void main(String[] args) {
        Student s1 = new Student("张三", 16);
        Student s2 = new Student("李四", 3);
        Student s3 = new Student("王五", 54);

        //使用TreeSet集合保存自定义的学生对象
        //并且根据年龄进行排序
        TreeSet<Student> treeSet = new TreeSet<>();
        treeSet.add(s1);
        treeSet.add(s2);
        treeSet.add(s3);

        //会报错
        //Student cannot be cast to class java.lang.Comparable
        for (Student s :
                treeSet) {
            System.out.println(s);
        }

        //因为TreeSet集合对元素自动排序,前提是元素实现了Comparable
        //元素实现Comparable,就具备自动排序的能力


    }

在compareTo方法中,this代表当前要存入的元素;参数o代表集合已经存在的元素。

返回值整数:放在红黑树右边

返回值负数: 放在红黑树左边

返回值0: 要存入的元素重复,不存入。

使用比较器

在TreeSet的构造方法中,传入比较器对象(通常传匿名内部类)

注意不要和Comparable搞混,Comparable是元素实现这个接口(自然排序),Comparator是构造方法(比较器排序)。

public static void main(String[] args) {
        TreeSet<Integer> set = new TreeSet<>(new Comparator<Integer>() {
            @Override
            //Integer o1 要存入的元素
            //Integer o2 已经存在的元素
            public int compare(Integer o1, Integer o2) {
                return o2 - o1;
            }
        });
        set.add(1);
        set.add(2);
        set.add(3);

        //默认是升序的
        //要求元素进行降序排序,可以在TreeSet的构造方法中,传入比较器对象(通常传匿名内部类)
        
        for (Integer i :
                set) {
            System.out.println(i);
        }
    }

Comparable 和 Comparator两种排序规则都存在时,Comparator比较器优先级更高

常见问题

public static void main(String[] args) {
        //要求降序,我们构造方法实现Comparator
        //因为返回值为int,我们相减的结果是Double类型,
        //又因为是重写不能改返回值
        //强转又会精度缺失
        TreeSet<Double> set = new TreeSet<>(new Comparator<Double>() {
            @Override
            public int compare(Double o1, Double o2) {
                Double result =  o2 - o1;
                if(result<0){
                    return  -1;
                }else if(result>0){
                    return 1;
                }else {
                    return 0;
                }
                //第二种写法
//                return Double.compare(o2 - o1);
            }
        });
        set.add(95.5);
        set.add(95.7);
        set.add(95.2);

        for (double d :
                set) {
            System.out.println(d);
        }
    }

最后

如果你对本文有疑问,你可以在文章下方对我留言,敬请指正,对于每个留言我都会认真查看。

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

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值