由浅入深理解java集合(二)——集合 Set

news2024/11/28 19:26:10

一、HashSet类
HashSet简介
HashSet是Set接口的典型实现,实现了Set接口中的所有方法,并没有添加额外的方法,大多数时候使用Set集合时就是使用这个实现类。HashSet按Hash算法来存储集合中的元素。因此具有很好的存取和查找性能。

HashSet特点
1.不能保证元素的排列顺序,顺序可能与添加顺序不同,顺序也有可能发生变化。
2.HashSet不是同步的,如果多个线程同时访问一个HashSet,则必须通过代码来保证其同步。
3.集合元素值可以是null。
除此之外,HashSet判断两个元素是否相等的标准也是其一大特点。HashSet集合判断两个元素相等的标准是两个对象通过equals()方法比较相等,并且两个对象的hashCode()方法返回值也相等。

写到这里,我们就要介绍下equals()和hashCode()方法了。

equals()和hashCode()
equals()
equals() 的作用是** 用来判断两个对象是否相等。**

equals() 定义在JDK的Object.java中。通过判断两个对象的地址是否相等(即,是否是同一个对象)来区分它们是否相等。源码如下:

public boolean equals(Object obj) {
       return (this == obj);
   }

既然Object.java中定义了equals()方法,这就意味着所有的Java类都实现了equals()方法,所有的类都可以通过equals()去比较两个对象是否相等。 但是,使用默认的“equals()”方法,等价于“==”方法。我们也可以在Object的子类中重写此方法,自定义“equals()”方法,在其中定义自己的判断逻辑,如果满足则返回true,不满足则返回false。下面我们自定义一个类 Person,并认为年龄,身高相等的两个Person对象,equals()方法比较结果相等。

public class Person {
    public int age;
    public int height;
    @Override
    public boolean equals(Object obj) {
        if (this == obj)
            return true;
        if (obj == null)
            return false;
        if (getClass() != obj.getClass())
            return false;
        Person other = (Person) obj;
        if (age != other.age)
            return false;
        if (height != other.height)
            return false;
        return true;
    }
}
public class EqualTest {
  public static void main(String[] args){
      Person p1 = new Person();
      Person p2 =new Person();
      System.out.println(p1.equals(p2));
  }

}

输出结果:

true

下面根据“类是否覆盖equals()方法”,将它分为2类。
(01) 若某个类没有覆盖equals()方法,当它的通过equals()比较两个对象时,实际上是比较两个对象是不是同一个对象。这时,等价于通过“==”去比较这两个对象,即两个对象的内存地址是否相同。
(02) 我们可以覆盖类的equals()方法,来让equals()通过其它方式比较两个对象是否相等。通常的做法是:若两个对象的内容相等,则equals()方法返回true;否则,返回fasle。

hashCode()

hashCode() 的作用是获取哈希码,也称为散列码;它实际上是返回一个int整数。这个哈希码的作用是确定该对象在哈希表中的索引位置。

hashCode() 定义在JDK的Object.java中,这就意味着Java中的任何类都包含有hashCode()
函数。虽然,每个Java类都包含hashCode() 函数。但是,仅仅当创建某个“类"的散列表时,该类的hashCode()
才有用。更通俗地说就是创建包含该类的HashMap,Hashtable,HashSet集合时,hashCode()
才有用。因为HashMap,Hashtable,HashSet就是散列表集合。

在散列表中,hashCode()作用是:确定该类的每一个对象在散列表中的位置;其它情况下类的hashCode() 没有作用。在散列表中hashCode() 的作用是获取对象的散列码,进而确定该对象在散列表中的位置。

hashCode()也分两种情况。一种是Object类中默认的方法,另一种是在子类中重写的方法。

(01) 若某个类没有覆盖hashCode()方法,当它的通过hashCode()比较两个对象时,实际上是比较两个对象是不是同一个对象。这时,等价于通过“==”去比较这两个对象,即两个对象的内存地址是否相同。
(02) 我们可以覆盖类的hashCode()方法,来让hashCode()通过其它方式比较两个对象是否相等。通常的做法是:若两个对象的内容相等,则hashCode()方法返回true;否则,返回fasle。

通过对以上两个方法的了解。我们可以接下来学习HashSet集合中如何判断两个元素是否相等?

HashSet中判断集合元素相等
两个对象比较 具体分为如下四个情况:

1.如果有两个元素通过equal()方法比较返回false,但它们的hashCode()方法返回不相等,HashSet将会把它们存储在不同的位置。

2.如果有两个元素通过equal()方法比较返回true,但它们的hashCode()方法返回不相等,HashSet将会把它们存储在不同的位置。

3.如果两个对象通过equals()方法比较不相等,hashCode()方法比较相等,HashSet将会把它们存储在相同的位置,在这个位置以链表式结构来保存多个对象。这是因为当向HashSet集合中存入一个元素时,HashSet会调用对象的hashCode()方法来得到对象的hashCode值,然后根据该hashCode值来决定该对象存储在HashSet中存储位置。

4.如果有两个元素通过equal()方法比较返回true,但它们的hashCode()方法返回true,HashSet将不予添加。

HashSet判断两个元素相等的标准:两个对象通过equals()方法比较相等,并且两个对象的hashCode()方法返回值也相等。

注意:HashSet是根据元素的hashCode值来快速定位的,如果HashSet中两个以上的元素具有相同的hashCode值,将会导致性能下降。所以如果重写类的equals()方法和hashCode()方法时,应尽量保证两个对象通过hashCode()方法返回值相等时,通过equals()方法比较返回true。

LinkedHashSet类
LinkedHashSet是HashSet对的子类,也是根据元素的hashCode值来决定元素的存储位置,同时使用链表维护元素的次序,使得元素是以插入的顺序来保存的。当遍历LinkedHashSet集合里的元素时,LinkedHashSet将会按元素的添加顺序来访问集合里的元素。但是由于要维护元素的插入顺序,在性能上略低与HashSet,但在迭代访问Set里的全部元素时有很好的性能。
注意:LinkedHashSet依然不允许元素重复,判断重复标准与HashSet一致。

补充:HashSet的实质是一个HashMap。HashSet的所有集合元素,构成了HashMap的key,其value为一个静态Object对象。因此HashSet的所有性质,HashMap的key所构成的集合都具备。可以参考后续文章中HashMap的相关内容进行比对。

二、TreeSet类
TreeSet简介
TreeSet是SortedSet接口的实现类,正如SortedSet名字所暗示的,TreeSet可以确保集合元素处于排序状态。此外,TreeSet还提供了几个额外的方法。

TreeSet的方法

comparator():返回对此 set 中的元素进行排序的比较器;如果此 set 使用其元素的自然顺序,则返回nullfirst():返回此 set 中当前第一个(最低)元素。
last(): 返回此 set 中当前最后一个(最高)元素。
lower(E e):返回此 set 中严格小于给定元素的最大元素;如果不存在这样的元素,则返回 nullhigher(E e):返回此 set 中严格大于给定元素的最小元素;如果不存在这样的元素,则返回 nullsubSet(E fromElement, E toElement):返回此 set 的部分视图,其元素从 fromElement(包括)到 toElement(不包括)。
headSet(E toElement):返回此 set 的部分视图,其元素小于toElement。
tailSet(E fromElement):返回此 set 的部分视图,其元素大于等于 fromElement。

TreeSet的排序方式
TreeSet中所谓的有序,不同于之前所讲的插入顺序,而是通过集合中元素属性进行排序方式来实现的。
TreeSet支持两种排序方法:自然排序和定制排序。在默认情况下,TreeSet采用自然排序。

1.自然排序
在讲自然排序之前,要先讲一下Comparable接口。

Java提供了一个Comparable接口,该接口里定义了一个compareTo(Object obj)方法,该方法返回一个整数值,实现该接口的类必须实现该方法,实现了该接口的类的对象就可以比较大小了。当一个对象调用该方法与另一个对象比较时,例如obj1.compareTo(obj2),如果该方法返回0,则表明两个对象相等;如果该方法返回一个整数,则表明obj1大于obj2;如果该方法返回一个负整数,则表明oj1小于obj2。

TreeSet会调用集合中元素所属类的compareTo(Object obj)方法来比较元素之间的大小关系,然后将集合元素按升序排列,即把通过compareTo(Object obj)方法比较后比较大的的往后排。这种方式就是自然排序。

Java的一些常用类已经实现了Comparable接口,并提供了比较大小的标准。例如,String按字符串的UNICODE值进行比较,Integer等所有数值类型对应的包装类按它们的数值大小进行比较。
除了这些已经实现Comparable接口类之外,如果试图把一个对象添加到TreeSet时,则该对象的类必须实现Comparable接口,否则就会出现异常。
注意:TreeSet中只能添加同一种类型的对象,否则无法比较,会出现异常。

TreeSet中判断集合元素相等
对于TreeSet集合而言,判断两个对象是否相等的唯一标准是:两个对象通过compareTo(Object obj)方法比较是否返回0——如果通过compareTo(Object obj)方法比较返回0,TreeSet则会认为它们相等,不予添加入集合内;否则就认为它们不相等,添加到集合内。
TreeSet是根据红黑树结构找到集合元素的存储位置。

2.定制排序
TreeSet的自然排序是根据集合元素中compareTo(Object obj)比较的大小,以升序排列。而定制排序是通过Comparator接口的帮助。该接口包含一个int compare(T o1,T o2)方法,该方法用于比较o1,o2的大小:如果该方法返回正整数,则表明o1大于o2;如果该方法返回0,则表明o1等于o2;如果该方法返回负整数,则表明o1小于o2。
如果要实现定制排序,则需要在创建TreeSet时,调用一个带参构造器,传入Comparator对象。并有该Comparator对象负责集合元素的排序逻辑,集合元素可以不必实现Comparable接口。下面具体演示一下这种用法:

public static void main(String[] args){
        Person p1 = new Person();
        p1.age =20;
        Person p2 =new Person();
        p2.age = 30;
        Comparator<Person> comparator = new Comparator<Person>() {

            @Override
            public int compare(Person o1, Person o2) {
                //年龄越小的排在越后面
                if(o1.age<o2.age){
                    return 1;
                }else if(o1.age>o2.age){
                    return -1;
                }else{
                    return 0;
                }
                
            }
        };
        TreeSet<Person> set = new TreeSet<Person>(comparator);
        set.add(p1);
        set.add(p2);
        System.out.println(set);
    }
[Person[age=30], Person[age=20]]

总结:无论使用自然排序还是定制排序,都可以通过自定义比较逻辑实现各种各样的排序方式。

注意:如果向TreeSet中添加了一个可变对象后,并且后面程序修改了该可变对象的实例变量,这将导致它与其他对象的大小顺序发生了改变,但TreeSet不会再次调整它们。下面程序演示这一现象:

TreeSet<Person> set = new TreeSet<Person>();
        Person p1 = new Person();
        p1.setAge(10);
        Person p2 =new Person();
        p2.setAge(30);
        Person p3 =new Person();
        p3.setAge(40);
        set.add(p1);
        set.add(p2);
        set.add(p3);
        System.out.println("初始年龄排序");
        System.out.println(set);
        //p1的年龄修改成60 最大
        p1.age = 60;
        System.out.println("修改p1年龄后集合排序");
        System.out.println(set);
        p2.age = 40;
        System.out.println("修改p2年龄后集合排序");
        System.out.println(set);
        Person p4 = new Person();

其中Person实现Comparable接口,将Person对象按照年龄从小到大升序排列。
输出结果:

初始年龄排序
[Person[age=10], Person[age=30], Person[age=40]]
修改p1年龄后集合排序
[Person[age=60], Person[age=30], Person[age=40]]
修改p2年龄后集合排序
[Person[age=60], Person[age=40], Person[age=40]]

可以看到并没有发生变化,而且如果修改后进行元素删除操作可能会不成功,具体比较复杂。总之,推荐不要修改放入TreeSet集合中元素的关键实例变量。
补充:TreeSet也是非线程安全的。

三、EnumSet类
EnumSet简介
EnumSet是一个专为枚举类设计的集合类,EnumSet中的所有元素都必须是指定枚举类型的枚举值,该枚举类型在创建EnumSet时显示或隐式地指定。EnumSet的集合元素也是有序的,EnumSet以枚举值在EnumSet类内的定义顺序来决定集合元素的顺序。

EnumSet特点
1.EnumSet集合不允许加入null元素。EnumSet中的所有元素都必须是指定枚举类型的枚举值。
2.EnumSet类没有暴露任何构造器来创建该类的实例,程序应该通过它提供的类方法来创建EnumSet对象。

EnumSet没有其他额外增加的方法,只是增加了一些创建EnumSet对象的方法。

EnumSet创建对象的方法
在这里插入图片描述
补充:EnumSet 也是非线程安全的。
四、HashSet、TreeSet和EnumSet的性能对比
EnumSet性能>HashSet性能>LinkedHashSet>TreeSet性能

EnumSet内部以位向量的形式存储,结构紧凑、高效,且只存储枚举类的枚举值,所以最高效。HashSet以hash算法进行位置存储,特别适合用于添加、查询操作。LinkedHashSet由于要维护链表,性能比HashSet差点,但是有了链表,LinkedHashSet更适合于插入、删除以及遍历操作。而TreeSet需要额外的红黑树算法来维护集合的次序,性能最次。

但是具体使用要考虑具体的使用场景。
当需要一个特定排序的集合时,使用TreeSet集合。
当需要保存枚举类的枚举值时,使用EnumSet集合。
当经常使用添加、查询操作时,使用HashSet。

当经常插入排序或使用删除、插入及遍历操作时,使用LinkedHashSet。

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

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

相关文章

Docker Swarm集群管理

秋风阁(https://focus-wind.com/) 文章目录 基本概念Swarm节点任务与服务 集群管理端口配置创建集群加入集群查看集群退出集群 节点设置节点标签设置节点信息查询 服务管理部署服务查看服务服务日志查看服务重启 Docker Swarm官方文档 Docker Swarm是Docker引擎原生内置的容器…

FE_函数(Function)的基本理解

1 函数&#xff08;Function&#xff09;的基本理解 函数就是在程序设计中&#xff0c;将一段代码封装起来&#xff0c;完成一个特定的功能&#xff0c;并给这段代码起一个名称&#xff0c;程序通过名称就可以执行这段代码。函数也是一个对象&#xff0c;也具有普通对象的功能…

华为测开面试记,三面被吊打,所幸最后Offer已到手

在互联网做了几年之后&#xff0c;去大厂“镀镀金”是大部分人的首选。大厂不仅待遇高、福利好&#xff0c;更重要的是&#xff0c;它是对你专业能力的背书&#xff0c;大厂工作背景多少会给你的简历增加几分竞争力。 但说实话&#xff0c;想进大厂还真没那么容易。最近面试华…

企业级信息系统开发讲课笔记4.3 Spring Boot两种全局配置和两种注解

文章目录 零、学习目标一、全局配置文件概述二、Application.properties配置文件&#xff08;一&#xff09;创建Spring Boot的Web项目PropertiesDemo&#xff08;二&#xff09;在application.properties里添加相关配置1、配置tomcat端口号和web虚拟路径2、对象类型的配置与使…

C++继承(一文学懂继承)——对象赋值转换、菱形虚拟继承

hello&#xff0c;这里是bangbang&#xff0c;今天来讲下继承。 面向对象三大特性&#xff1a;封装、继承、多态。 目录 1. 继承的概念及定义 1.1 继承的概念 1.2 继承定义 1.2.1 定义格式 1.2.2 继承关系和访问限定符 1.2.3 继承基类成员访问方式的变化 2. 基类和派生类对…

波士顿房价数据集怎么不见了?

波士顿数据下载 消失的波士顿 OoO 做线性回归的同学大概率会用到一个数据集&#xff0c;即波士顿房价数据集&#xff0c;然而当你从sklearn下载该数据集时&#xff0c;你会惊讶地发现居然下载不了了&#xff01;&#xff01;&#xff01;起初我以为是是什么别的原因导致数据集可…

云计算之OpenStack基础

云计算之OpenStack基础 一、OpenStack基础知识二、虚拟化2.1 虚拟化类型2.1.1 Ⅰ型虚拟化2.1.2 Ⅱ型虚拟化2.1.3 比较 2.2 KVM&#xff08;Ⅱ型虚拟化&#xff09;2.2.1 基本概念2.2.2 Libvirt2.2.3 CPU虚拟化2.2.4 内存虚拟化2.2.5 存储虚拟化2.2.5.1 目录类型的 Storage Pool…

【教程】配置NFS共享直接访问目标服务器的存储目录

转载请注明出处&#xff1a;小锋学长生活大爆炸[xfxuezhang.cn] 目录 前景提要 NFS安装教程 旧服务器上 新服务器上 验证安装 优缺点 扩展 前景提要 我们新服务器的硬盘容量很小&#xff0c;但旧服务器的硬盘容量很大。如何在不拔硬盘的情况下&#xff0c;直接已本地目录…

分区计量管理项目应用

为充分发挥分区计量管理项目在漏损控制的效用&#xff0c;应构建科学完备的应用体系&#xff0c;如下图 分区计量应用体系 1. 基于水量平衡分析的漏损现状评估方法 分区计量管理项目通过监控分析DMA 分区内流量、压力、水质、大用户用水等情况&#xff0c;结合营业抄收系统的营…

【内网渗透】春秋云镜 Tsclient WP

前言 mssql连接和攻击、windows提权、令牌窃取、镜像劫持 flag1 上来就扫给的地址 nmap: 结合题目的mssql知识点&#xff0c;估计是从这里入手了&#xff0c;尝试爆破用户名和密码&#xff0c;这里先用fscan来爆&#xff0c;可以直接爆出来&#xff08;别的工具也可&#x…

0.96寸oled显示坏苹果(badapple)

前言 俗话说&#xff1a;有屏幕的地方就会有badapple。 下面带来使用0.96寸OLED屏幕显示badapple的教程。 1、获取视频 首先从网上下载badapple的视频&#xff0c;下载地址&#xff1a;badapple 2、抓取视频图片 使用OLED播放视频的思想就是将视频分成一张一张的图片然后进行…

叉乘在图形学中的几何意义 ---- 判断一个点是否在三角形内

1 叉乘是什么 先简单介绍一下叉乘(cross product)&#xff1a; a → b → \overrightarrow{a} \times \overrightarrow{b} a b &#xff0c;其结果&#xff0c;还是一个向量。 其方向&#xff0c;符合右手螺旋定则&#xff08;右手手指头从a转向b&#xff0c;看大拇指指向哪…

声音合成——Foley Sound——DECASE项目——多模态智能感知与应用——VariantAutoencoder(VAE)代码实现(7)

文章目录 概述VAE代码实现关闭eager execution修改bottlenectk组件修改loss损失函数 Preprocessline模块实现Loader模块Padder模块LogSpectrogramExtractor模块MinMaxNormaliser模块Saver模块PreprocessPipeLine模块知识补充property修饰词 train训练模块load_fsdd模块train模块…

针对基于智能卡进行认证的活动目录攻击

最近&#xff0c;我参与了一项攻击基于智能卡的活动目录的工作。实际上&#xff0c;你根本不需要使用物理智能卡来验证登录这个活动目录。证书的属性决定了它是否可以用于基于智能卡进行登录。因此&#xff0c;如果你能获得相应的私钥&#xff0c;那么就可以绕过智能卡的验证实…

ubuntu 22.04 安装 Docker Desktop 及docker介绍

目录 一、Docker Desktop 安装 1、我们先去官网下载安装包 2、Install Docker Desktop on Ubuntu 3、Launch Docker Desktop 二、Docker 介绍 什么是docker 如何使用docker docker是如何工作的 docker build docker run docker pull 一、Docker Desktop 安装 1、我们先…

网络应用技术师技能考试试题+讲解

作者简介:一名云计算网络运维人员、每天分享网络与运维的技术与干货。 座右铭:低头赶路,敬事如仪 个人主页:网络豆的主页​​​​​​ 前言 本章将会讲解网络应用技术师技能考试试题 一.正文 一、单选题(每题1分,满分50分) 1. 设信号的波特率为800Baud,采用幅度…

肠道微生物群:心力衰竭的新治疗靶点

谷禾健康 心力衰竭 Heart Failure&#xff08;HF&#xff09; 心力衰竭是一种严重的心血管疾病&#xff0c;其特点是较高的发病率和死亡率&#xff0c;同时也会带来高昂的医疗成本。 一般都认为心力衰竭是老年人的疾病&#xff0c;但其实心力衰竭已经呈现年轻化趋势。 以上症状…

a标签属性href的多种写法

众所周知&#xff0c;a标签的最重要功能是实现超链接和锚点。而且&#xff0c;大多数人认为a标签最重要的作用是实现超链接&#xff0c;其实不单单是实现超链接的方法&#xff0c;今天新起点博客就来整理下a标签中href的几种用法。 1、a href“[removed]js_method();” 这是常用…

即拼七人拼团系统开发模式,为什么这么火?

即拼七人拼团模式主要是结合了拼团模式的奖励机制和二二复制系统的排位玩法&#xff0c;将产品销售中的利润最大化让利于拼团的用户&#xff0c;刺激用户主动分享推广&#xff0c;以解决平台引流和用户活跃度的问题。 具体来说&#xff0c;即拼七人拼团模式就是用户进入平台购买…

崩坏:星穹铁道私人服务器搭建教程

准备工具&#xff1a; 一台 Windows 系统服务器 《崩坏&#xff1a;星穹铁道》启动器 《崩坏&#xff1a;星穹铁道》客户端 如果以上各项你都准备好了的话&#xff0c;现在就开始吧&#xff01; 首先完全解压你的《崩坏&#xff1a;星穹铁道》客户端以及SR启动器。 解压完成…