单列集合之Set集合以及各种实现类

news2024/11/13 16:08:05

Set集合

Set接口也是Collection单列结合的一个子接口,set集合中没有提供其他额外的方法,但是相比较Collection集合新增了其他的特性。所有实现了Set接口的类都可以叫做Set集合。

Coliection接口中的方法:Collection集合的方法

Set集合不允许包含相同的元素,也就是元素唯一,其中元素的存储顺序大多数无序,部分有序。

Set集合的遍历方式和Collection集合一样:foreachIterator

Set接口的常用实现类:HashSetTreeSetLinkedHashSet

Set集合结构图

HashSet集合

HashSet类是Set接口的典型实现类,在使用Set集合的时候大多数使用的就是这个实现类。

java.util.HashSet底层使用的java.util.HashMap中的key进行存储数据,底层的实现是一个Hash表

简化Hash表
在JDK1.7以及之前,hash表存链表的方式是使用的头插法的方式,但是这样容易导致死循环(多线程)。
在JDK1.8及之后,链表节点由头插法改成了尾插法

HashSet是按照Hash表进行存储数据,采用了Hash算法,因此具有很好的查询和查找性能。

如何使用HashSet集合判断两个元素是否相同?
HashSet集合判断两个元素相等的标准: 1、两个对象通过hashCode()方法比较hashCode是否相同, 2、比较两个元素对象的equals()方法返回值是否为true。 因此,存储到HashSet集合中的元素要重写hashCode和equals方法。

1、创建生日类:

public class Birthday {

    private int year;

    private int month;

    private int day;

    public Birthday() {
    }

    public Birthday(int year, int month, int day) {
        this.year = year;
        this.month = month;
        this.day = day;
    }

    @Override
    public int hashCode() {
        final int prime = 31;
        int result = 1;
        result = result * prime + year;
        result = result * prime + month;
        result = result *prime + day;
        return result;
    }

    @Override
    public boolean equals(Object obj) {
        if (this == obj){return true;}
        if (obj == null){return false;}
        if (this.getClass() != obj.getClass()){return false;}
        Birthday birthday = (Birthday) obj;
        if (this.year!= birthday.year){return false;}
        if (this.month != birthday.month){return false;}
        if (this.day != birthday.day){return false;}
        return true;
    }

    @Override
    public String toString() {
        return "Birthday{" +
                "year=" + year +
                ", month=" + month +
                ", day=" + day +
                '}';
    }

    public int getYear() {
        return year;
    }

    public void setYear(int year) {
        this.year = year;
    }

    public int getMonth() {
        return month;
    }

    public void setMonth(int month) {
        this.month = month;
    }

    public int getDay() {
        return day;
    }

    public void setDay(int day) {
        this.day = day;
    }
}

2、创建员工类:

public class Employee {

    private Integer id;

    private String name;

    private Birthday birthday;

    public Employee() {
    }

    public Employee(Integer id, String name, Birthday birthday) {
        this.id = id;
        this.name = name;
        this.birthday = birthday;
    }

    @Override
    public int hashCode() {
        final int prime = 31;
        int result = 1;
        result = result * prime + ((id == null) ? 0 : id.hashCode());
        result = result * prime + ((name == null) ? 0 : name.hashCode());
        result = result * prime + ((birthday == null) ? 0 : birthday.hashCode());
        return result;
    }

    @Override
    public boolean equals(Object obj) {
        if (this==obj){return true;}
        if (obj==null){return false;}
        if (this.getClass()!=obj.getClass()){return false;}
        Employee employeeOther = (Employee) obj;
        if (this.id==null){
            if (employeeOther.id!=null){return false;}
        }else if (!this.id.equals(employeeOther.id)){return false;}
        if (this.name==null){
            if (employeeOther.name!=null){return false;}
        }else if (!this.name.equals(employeeOther.name)){return false;}
        if (this.birthday==null){
            if (employeeOther.birthday!=null){return false;}
        }else if (!this.birthday.equals(employeeOther.birthday)){return false;}
        return true;
    }

    @Override
    public String toString() {
        return "Employee{" +
                "id=" + id +
                ", name='" + name + '\'' +
                ", birthday=" + birthday +
                '}';
    }

    public Integer getId() {
        return id;
    }

    public void setId(Integer id) {
        this.id = id;
    }

    public String getName() {
        return name;
    }

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

    public Birthday getBirthday() {
        return birthday;
    }

    public void setBirthday(Birthday birthday) {
        this.birthday = birthday;
    }
}

3、测试Set集合:

public class HashSetTest {
    public static void main(String[] args) {
        Set<Employee> set = new HashSet<>();

        set.add(new Employee(1001,"员工亿",new Birthday(1998,10,10)));
        set.add(new Employee(1001,"员工亿",new Birthday(1998,10,10)));
        set.add(new Employee(1002,"员工万",new Birthday(1999,11,6)));
        set.add(new Employee(1003,"员工富",new Birthday(1998,10,7)));

        for (Employee employee : set) {
            System.out.println("employee = " + employee);
        }
    }
}

结果:
HashSet测试结果
注意:可以看出来输出的结果和存入的顺序不一致,重复的元素只输出一个

LinkedHashSet集合

LinkedHashSet集合HashSet集合子类,也是位于java.util.LinkedHashSet包下的一个实现类。LinkedHashSet是在HashSet的基础上为每一个元素添加了beforeafter属性,保证了添加元素的前后顺序。LinkedHashSet的数据存储结构是:Hash表+链表。对于插入性能略低于HashSet,但是实现了有序,在迭代访问元素时有很好的性能。

LinkedHashSet的存储结构图:
LinkedHashSet的存储结构图
代码测试:

public class LinkedHashSetTest {

    public static void main(String[] args) {
        Set<Employee> linkedHashSet = new LinkedHashSet<>();

        linkedHashSet.add(new Employee(1001,"员工甲",new Birthday(2000,1,1)));
        linkedHashSet.add(new Employee(1002 ,"员工乙",new Birthday(2000,2,2)));
        linkedHashSet.add(new Employee(1003,"员工丙",new Birthday(2000,3,3)));

        Iterator<Employee> iterator = linkedHashSet.iterator();
        while (iterator.hasNext()){
            Employee employee = iterator.next();
            System.out.println("employee = " + employee);
        }
    }

}

结果:
LinkedHashSet测试结果
这里可以看到结果是和存储数据的时候一致。

TreeSet集合

在上面的LinkedHashSet中实现了Set集合的有序性,而在TreeSet集合中实现了Set集合中元素的排序

TreeSet集合是位于java.util包下的一个集合类,底层结构维护的一个TreeMap,是基于红黑树实现的一个集合。

红黑树:一种相对平衡的二叉树,查询效率高于链表,增删效率高于数组。

二叉搜索树:每个节点的左边所有子节点都小于或等于节点本身,每个节点的右边结点都大于或等于节点本身。

平衡二叉树:每个节点的左右子树高度差不能超过1

二叉树的遍历方式有:

  • 前序遍历:中-左-右
  • 中序遍历:左-中-右
  • 后序遍历:左-右-中

在TreeSet集合中采用的遍历方式是中序遍历

二叉树

  • 前序遍历:33,15,11,6,12,18,40,34,62
  • 中序遍历:6,11,12,15,18,33,34,40,62
  • 后序遍历:6,12,11,18,15,34,62,40,33

TreeSet集合的特点:

  • 元素唯一且无序
  • 实现了排序

在TreeSet集合中如何实现排序的呢?

  • 按照字符串的Unicode编码值进行排序
  • 按照自定义排序:存入的元素实现Comparable接口中的重写compareTo(Object obj)方法进行排序
  • 按照定制排序:在集合创建的过程中,构造器传入一个新建的Comparator接口,也就是匿名内部类的形式重写compare(Object o1,Object o2)方法
  1. 按照Unicode编码进行排序:
public class TreeSetTest {

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

        treeSet.add("set");
        treeSet.add("hashSet");
        treeSet.add("linkedHashSet");
        treeSet.add("treeSet");

        /*treeSet.add(1);
        treeSet.add(7);
        treeSet.add(5);
        treeSet.add(3);
        System.out.println("treeSet = " + treeSet); //treeSet = [1, 3, 5, 7]*/

        System.out.println("treeSet = " + treeSet); //treeSet = [hashSet, linkedHashSet, set, treeSet]
    }
}

String类也实现了Comparable接口

  1. 使用自定义的默认排序
public class Employee implements Comparable<Employee>{

    private Integer id;

    private String name;

    @Override
    public int compareTo(Employee o) {
        int i = o.id.compareTo(this.id)==0? 0:o.id.compareTo(this.id);
        i = o.name.compareTo(this.name)==0? i:o.name.compareTo(this.name);
        return i;
    }

    public Employee() {
    }

    public Employee(Integer id, String name) {
        this.id = id;
        this.name = name;
    }
    @Override
    public int hashCode() {
        final int prime = 31;
        int result = 1;
        result = result * prime + ((id == null) ? 0 : id.hashCode());
        result = result * prime + ((name == null) ? 0 : name.hashCode());
        return result;
    }

    @Override
    public boolean equals(Object obj) {
        if (this==obj){return true;}
        if (obj==null){return false;}
        if (this.getClass()!=obj.getClass()){return false;}
        Employee employeeOther = (Employee) obj;
        if (this.id==null){
            if (employeeOther.id!=null){return false;}
        }else if (!this.id.equals(employeeOther.id)){return false;}
        if (this.name==null){
            if (employeeOther.name!=null){return false;}
        }else if (!this.name.equals(employeeOther.name)){return false;}
        return true;
    }

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

    public Integer getId() {
        return id;
    }

    public void setId(Integer id) {
        this.id = id;
    }

    public String getName() {
        return name;
    }

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

}

测试:

public class TreeSetTest {

    public static void main(String[] args) {
        TreeSet<Employee> treeSet = new TreeSet<>();
        
        treeSet.add(new Employee(1001,"员工甲"));
        treeSet.add(new Employee(1002 ,"员工乙"));
        treeSet.add(new Employee(1003,"员工丙"));

        System.out.println("treeSet = " + treeSet);

    }
}

注意:这里使用的是Employee重写的compareTo方法的排序规则

结果演示:

实现Comparable接口的排序结果

  1. 使用定制排序规则

不需要实现Comparable接口

public class Birthday {

    private int year;

    private int month;

    private int day;

    public Birthday() {
    }

    public Birthday(int year, int month, int day) {
        this.year = year;
        this.month = month;
        this.day = day;
    }

    @Override
    public int hashCode() {
        final int prime = 31;
        int result = 1;
        result = result * prime + year;
        result = result * prime + month;
        result = result *prime + day;
        return result;
    }

    @Override
    public boolean equals(Object obj) {
        if (this == obj){return true;}
        if (obj == null){return false;}
        if (this.getClass() != obj.getClass()){return false;}
        Birthday birthday = (Birthday) obj;
        if (this.year!= birthday.year){return false;}
        if (this.month != birthday.month){return false;}
        if (this.day != birthday.day){return false;}
        return true;
    }

    @Override
    public String toString() {
        return "Birthday{" +
                "year=" + year +
                ", month=" + month +
                ", day=" + day +
                '}';
    }

    public int getYear() {
        return year;
    }

    public void setYear(int year) {
        this.year = year;
    }

    public int getMonth() {
        return month;
    }

    public void setMonth(int month) {
        this.month = month;
    }

    public int getDay() {
        return day;
    }

    public void setDay(int day) {
        this.day = day;
    }
}

实现匿名内部类

public class TreeSetTest {

    public static void main(String[] args) {
        TreeSet<Birthday> treeSet = new TreeSet<>(new Comparator<Birthday>() {
            @Override
            public int compare(Birthday o1, Birthday o2) {
                int i =o1.getDay()-o2.getDay();
                i = (o1.getMonth()-o2.getMonth())==0? i:(o1.getMonth()-o2.getMonth());
                i = (o1.getYear()-o2.getYear()) == 0? i:(o1.getYear()-o2.getYear());
                return i;
            }
        });

        treeSet.add(new Birthday(1998,10,10));
        treeSet.add(new Birthday(1999,11,6));
        treeSet.add(new Birthday(1998,10,7));

        System.out.println("treeSet = " + treeSet);
    }
}

结果演示:
TreeSet匿名内部类结果演示
注意:在使用TreeSet集合进行出元素时,存入的元素会显赫已有的元素进行判断,如果判断结果为负数,则放到左边,判断结果为正数放到右边,如果为0则覆盖原来的值。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/448537.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

更全面的对比GPT4和Claude对MLIR的掌握能力

本文构造了20个MLIR基础概念的问题以及使用OneFlow IR转换为Tosa IR的5个代码段来评测GPT4和Claude对于MLIR的掌握能力&#xff0c;我的结论是对于基础概念的理解Claude整体上和GPT4持平&#xff0c;而在阅读相关代码片段时Claude表现出了比GPT4更强一点的理解能力。 0x0. 前言…

【Python】读取r语言数据+NMF算法(完整代码+详细注释)

目录 依赖库代码功能完整代码总结 欢迎关注 『Python』 系列&#xff0c;持续更新中 欢迎关注 『Python』 系列&#xff0c;持续更新中 算法部分源码是我的数模兄弟想要深入研究nmf算法方面的内容发给我让我跑的 参考自博文 https://blog.csdn.net/atease0001/article/details/…

计及光伏电站快速无功响应特性的分布式电源优化配置方法(Matlab代码实现)

&#x1f4a5; &#x1f4a5; &#x1f49e; &#x1f49e; 欢迎来到本博客 ❤️ ❤️ &#x1f4a5; &#x1f4a5; &#x1f3c6; 博主优势&#xff1a; &#x1f31e; &#x1f31e; &#x1f31e;博客内容尽量做到思维缜密&#xff0c;逻辑清晰&#xff0c;为了方便读者。 …

JS的函数定义和参数

文章目录 一、JavaScript 函数定义二、JavaScript 函数参数总结 一、JavaScript 函数定义 JavaScript 使用关键字 function 定义函数。 函数可以通过声明定义&#xff0c;也可以是一个表达式。 函数声明 function functionName(parameters) {执行的代码 }函数声明后不会立即…

【C++内联函数】

目录 前言内联函数的概念内联函数的特性内联函数的总结 前言 假设有这样的一个场景&#xff0c;有一个代码量不足三行的函数被调用了一万次&#xff0c;现在让你做优化&#xff0c;你会怎么考虑优化&#xff1f; 我们都知道函数调用是有时间和空间开销的。程序在执行一个函数之…

Ansys Lumerical | 使用 STACK 仿真抗反射偏振器件

1、说明 在本示例中&#xff0c;我们将展示使用 Lumerical STACK 求解器来设计抗反射圆偏振器&#xff0c;以减少 OLED 显示器的环境光反射。 2、综述 OLED 显示器的底部金属电极可以用于增强光提取效率&#xff0c;然而它也会带来环境光反射的不利影响&#xff0c;导致显示器在…

深度学习-基础(二)-numpy中的轴操作

背景 使用Pytorch进行学习&#xff0c;少不了跟numpy打交道&#xff0c;比如数据集中去除通道reduction只有做加法运算等&#xff0c;但是numpy轴操作&#xff0c;很少有人讲清楚&#xff0c;此处加以梳理。 轴的概念 轴用来为超过一维的数组定义的属性&#xff0c;二维数据…

【Atlas500】华为500小站预配置

目录 基础配置解决配置能力项未开启问题 基础配置 1.网线连接盒子 2.1口IP:192.168.2.111 2口IP&#xff1a;192.168.3.111 3.登临网页https://192.168.2.111 输入用户名和密码&#xff08;管理端用户&#xff09;。 ● 默认用户名&#xff1a;admin ● 默认密码&#xff1a;H…

Eclipse安装插件及所有插件下载地址汇总

Eclipse安装插件及所有插件下载地址汇总 插件的意义安装插件各插件下载地址汇总kepler&#xff08;Eclipse配置本地安装好的Tomcat时使用的插件&#xff09;另一个插件名称 插件的意义 自认是为了解耦&#xff0c;使使用eclipse的人可以依据自己所需&#xff0c;有针对性的下载…

MySQL_第04章_运算符

第04章_运算符 讲师&#xff1a;尚硅谷 - 宋红康&#xff08;江湖人称&#xff1a;康师傅&#xff09; 官网&#xff1a; http://www.atguigu.com 1. 算术运算符 算术运算符主要用于数学运算&#xff0c;其可以连接运算符前后的两个数值或表达式&#xff0c;对数值或表达式…

Node第三方包 【mysql2】

文章目录 &#x1f31f;前言&#x1f31f;访问数据库&#x1f31f;MySQL驱动&#x1f31f;mysql2&#x1f31f;安装 &#x1f31f;方法&#x1f31f;连接数据库&#x1f31f;通过 query() 方法执行SQL语句&#x1f31f;通过 execute() 方法执行SQL语句 &#x1f31f;使用连接池…

研报精选230419

目录 【行业230419东吴证券】AACR2023本土药企临床进展&#xff1a;“秒懂”全球创新药系列研究之会议追踪 【行业230419浙商证券】大制造行业估值手册&#xff1a;周度数据跟踪 【行业230419东方财富证券】2023上海车展前瞻&#xff1a;自主争先&#xff0c;迎接智能电动新时代…

#Chrome扩展程序开发教程--03:Manifest

#Chrome扩展程序开发教程--03&#xff1a;Manifest 引言1、基本介绍2、必须属性3、重要属性3.1、permissions、host_permissions、optional_permissions、optional_host_permissions3.2、background3.3、content_scripts3.4、action3.5、options_page3.6、options_ui3.7、icons…

C++ Linux Web Server 面试基础篇-操作系统(四、线程通信)

⭐️我叫忆_恒心&#xff0c;一名喜欢书写博客的在读研究生&#x1f468;‍&#x1f393;。 如果觉得本文能帮到您&#xff0c;麻烦点个赞&#x1f44d;呗&#xff01; 近期会不断在专栏里进行更新讲解博客~~~ 有什么问题的小伙伴 欢迎留言提问欧&#xff0c;喜欢的小伙伴给个三…

TiDB实战篇-Data Migration (DM) 同步数据

目录 简介 原理 任务管理 ​编辑同步拓扑示例 使用场景 限制 硬件配置 安装&升级 部署 生成配置文件 生成配置文件模板 更具自身的机器修改 部署 启动集群&查看集群 实战 上游数据库前提 配置mysql的相关配置 编写DM的MySQL相关配置 把MySQL和DM …

【JavaEE】Bean的作用域和生命周期

目录 Bean的作用域 作用域分类 设置作用域 通过注解设置 通过配置文件设置 Bean的生命周期 Bean的作用域 Bean的作用域是指&#xff1a;在整个Spring容器中Bean的行为模式。这个模式有六种。 作用域分类 singleton&#xff1a;单例作用域。 在这个模式下&#xff0c;容器…

网络地址转换应用

如图所示,企业使用一台AR 路由器作为出口设备,路由器配置NAT Outbound为私网用户提供访问Internet服务,同时配置NAT Server将私网WEB服务器发布到公网上,对外网用户提供服务。运营商仅为该单位分配了一个公网IP,此地址既作为AR出接口的IP地址,也作为NAT Outbound和NAT Se…

【Atlas500】华为Atals MindStudio配置指南

目录 安装推理用ubuntu版docker配置docker内的ssh服务安装ubuntu系统中的依赖项&#xff1a;检查root用户的umask安装依赖项安装CANN连接到CANN Setting 安装推理用ubuntu版docker 华为的atlas500自带的欧拉系统是一个裁剪系统&#xff0c;一般通过在其上运行官方的ubuntu dock…

【消息队列】Kafka高水位和Leader Epoch原理

什么是高水位 首先高水位也就是HW&#xff0c;而对应的有LEO&#xff0c;其实这都是Kafka副本中针对位移的概念&#xff0c;其目的就是为了保证多副本间数据的一致性。 LEO &#xff08;Log End Offet&#xff09;&#xff1a;每个副本的最后一个offset&#xff0c;LEO其实就是…

你猜我猜不猜 (猜数字游戏) 快来小玩一把叭

&#x1f929;本文作者&#xff1a;大家好&#xff0c;我是paperjie&#xff0c;感谢你阅读本文&#xff0c;欢迎一建三连哦。 &#x1f970;内容专栏&#xff1a;这里是《C语言》专栏&#xff0c;笔者用重金(时间和精力)打造&#xff0c;将C语言知识一网打尽&#xff0c;希望可…