【Java基础】Set接口的子类


【Java基础】集合框架
【Java基础】Collection子接口:Set


HashSet

HashSet的特点
  • 底层数据结构是哈希表
    • 是一个元素为链表的数组
  • 对集合的迭代顺序不作任何保证
    • 不保证元素的存储和取出顺序一致
  • 元素唯一
    • 依赖hashCode()和equals()方法
如何保证元素唯一性的呢?

在这里插入图片描述
HashSet集合添加一个元素的过程:

  • 首先比较哈希值是否相同
    • 相同:继续执行equals()方法
      • 返回true:说明元素重复就不添加
      • 返回false:说明元素不重复就添加到集合
    • 不同:就直接把元素添加到集合
HashSet的案例
1、存储字符串并遍历

问题: 为什么存储字符串的时候,字符串内容相同的只存储了一个呢?
原因: HashSet底层依赖hashCode()和equals()方法,String类重写了hashCode()和equals()方法,可以把内容相同的字符串去掉只留一个;

import java.util.HashSet;

public class HashSetDemo1 {
    public static void main(String[] args) {
        //创建集合对象
        HashSet<String> hs = new HashSet<String>();
        //把字符串添加到集合
        hs.add("hello");
        hs.add("world");
        hs.add("java");
        hs.add("world");
        //遍历集合
        for (String s : hs) {
            System.out.println(s);
        }
    }
}

/*
  world
  java
  hello
*/
2、存储自定义对象并遍历

要求: 如果两个对象的成员变量值都相同,则为同一个元素;
问题: 目前是不符合要求的,因为HashSet底层依赖hashCode()和equals()方法,而学生类中没有重写这两个方法,默认使用的是Object类的,此时他们的哈希值是不会一样的,根本就不会继续判断,就直接把元素添加到集合。
解决方案: 重写学生类中的hashCode()和equals()方法,自动生成即可;

//定义学生类Student-->省略

//测试类:
import java.util.HashSet;

class HashSetDemo2 {
    public static void main(String[] args) {
        //创建集合对象
        HashSet<Student> hs = new HashSet<Student>();
        //创建学生对象
        Student s1 = new Student("张三", 18);
        Student s2 = new Student("李四", 20);
        Student s3 = new Student("王五", 22);
        Student s4 = new Student("赵六", 24);
        Student s1 = new Student("张三", 18);
        //把学生添加到集合
        hs.add(s1);
        hs.add(s2);
        hs.add(s3);
        hs.add(s4);
        for (Student s : hs) {
            System.out.println(s.getName() + "," + s.getAge());
        }
    }
}
HashSet集合的使用步骤
  • 定义自定义对象类
  • 创建集合对象
  • 创建元素对象
  • 把元素添加到集合
  • 遍历集合
  • 重写自定义对象类中的hashCode()和equals()方法(自动生成即可)

LinkedHashSet

LinkedHashSet的特点
  • 底层数据结构是链表和哈希表
  • 具有可预测的迭代次序
    • 元素的存储和取出顺序一致的
  • 元素有序唯一
    • 由链表保证元素有序
    • 由哈希表保证元素唯一性

TreeSet

TreeSet的特点
  • 底层数据结构是红黑树
    • 是一个自平衡的二叉树
  • 能够对元素按照一定的规则进行排序
  • 元素排序
    • 自然排序
    • 比较器排序
  • 元素唯一
    • 根据比较的返回值是否是0来决定
如何保证元素排序的呢?
  • 使用元素的自然顺序对元素进行排序(自然排序)
  • 根据创建set时提供的Comparator进行排序(比较器排序)
  • 具体取决于使用的构造方法
1、自然排序
  • TreeSet()
    • 构造一个新的,空的树组,根据其元素的自然排序进行排序;
  • 让元素所属的类实现Comparable接口,重写compareTo(T o)方法;
  • 比较两个对象是否相同的标准为:compareTo()返回0
2、比较器排序
  • TreeSet(Comparator<? super E> comparator)
    • 构造一个新的,空的树集,根据指定的比较器进行排序;
  • 让集合构造方法接收Comparator的实现类对象,重写compare(T o1,T o2)方法;
  • 比较两个对象是否相同的标准为:compare()返回0
TreeSet的案例
1、存储Integer对象并遍历
import java.util.TreeSet;

public class TreeSetDemo1 {
    public static void main(String[] args) {
        //创建集合对象
        //自然顺序进行排序
        TreeSet<Integer> ts = new TreeSet<Integer>();
        //创建元素并添加到集合
        ts.add(20);  //自动装箱
        ts.add(18);
        ts.add(23);
        ts.add(19);
        ts.add(18);
        //遍历集合
        for (Integer i : ts) {
            System.out.println(i);
        }
    }
}
2、存储自定义对象并遍历

要求: 按照姓名的长度排序

2.1、使用自然排序
  • 存储学生对象并遍历, 创建TreeSet集合使用无参构造方法;
  • 用TreeSet集合存储自定义对象, 无参构造方法使用的是自然排序对元素进行排序的;
  • 就是让元素所属的类实现Comparable接口,重写compareTo(T o)方法;
  • 重写方法时,一定要注意排序规则必须按照要求的主要条件和次要条件来写;

学生类:

//实现自然排序接口
public class Student implements Comparable<Student> {

    //成员变量:name age  -->省略
    //构造方法:无参 带参  -->省略
    //成员方法:getXxx() setXxx()  -->省略
    
    @Override
    public int compareTo(Student s) {
        //主要条件:姓名的长度
        int num = this.name.length() - s.name.length();
        //次要条件:姓名的内容
        int num2 = num == 0 ? this.name.compareTo(s.name) : num;
        //次要条件:年龄
        int num3 = num2 == 0 ? this.age - s.age : num2;
        return num3;
    }    
}

测试类:

import java.util.TreeSet;

public class TreeSetDemo2 {
    public static void main(String[] args) {
        //创建集合对象
        TreeSet<Student> ts = new TreeSet<Student>();
        //创建学生对象
        Student s1 = new Student("lisi", 20);
        Student s2 = new Student("zhangsan", 18);
        Student s3 = new Student("zhaoliu", 24);
        Student s4 = new Student("wangwu", 20);
        Student s5 = new Student("lisi", 22);
        Student s6 = new Student("zhangsan", 18);
        //把学生添加到集合
        ts.add(s1);
        ts.add(s2);
        ts.add(s3);
        ts.add(s4);
        ts.add(s5);
        ts.add(s6);
        //遍历集合
        for (Student s : ts) {
           System.out.println(s.getName() + "," + s.getAge());
        }
    }
}
2.2、使用比较器排序
  • 存储学生对象并遍历, 创建TreeSet集合使用带参构造方法;
  • 用TreeSet集合存储自定义对象,带参构造方法使用的是比较器排序对元素进行排序的;
  • 就是让集合构造方法接收Comparator的实现类对象,重写compare(T o1,T o2)方法;
  • 重写方法时,一定要注意排序规则必须按照要求的主要条件和次要条件来写;
//定义学生类Student-->省略

//测试类:
import java.util.Comparator;
import java.util.TreeSet;

public class TreeSetDemo2 {
    public static void main(String[] args) {
        //创建集合对象
        TreeSet<Student> ts = new TreeSet<Student>(new Comparator<Student>() {
            @Override
            public int compare(Student s1, Student s2) {
                //主要条件:姓名的长度
                int num = s1.getName().length() - s2.getName().length();
                //次要条件:姓名的内容
                int num2 = num == 0 ? s1.getName().compareTo(s2.getName()) : num;
                //次要条件:年龄
                int num3 = num2 == 0 ? s1.getAge() - s2.getAge() : num2;
                return num3;
            }
        });
        //创建学生对象
        Student s1 = new Student("lisi", 20);
        Student s2 = new Student("zhangsan", 18);
        Student s3 = new Student("zhaoliu", 24);
        Student s4 = new Student("wangwu", 20);
        Student s5 = new Student("lisi", 22);
        Student s6 = new Student("zhangsan", 18);
        //把学生添加到集合
        ts.add(s1);
        ts.add(s2);
        ts.add(s3);
        ts.add(s4);
        ts.add(s5);
        ts.add(s6);
        //遍历集合
        for (Student s : ts) {
           System.out.println(s.getName() + "," + s.getAge());
        }
    }
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值