I学霸官方免费教程三十二:Java集合框架之Set集合-程序员宅基地

技术标签: java  开发工具  数据结构与算法  

Set接口

Set集合是无序的、元素不可重复的结合
常用集合类有HashSet和TreeSet


HashSet类
常用的两种List集合各有各的优点,那么有没有同时具备这两种List集合的优点的集合呢?答案是肯定的,就是Set集合。

实例:
package collection.set.hashSet;
import java.util.HashSet;
import java.util.Iterator;
/**
 * 演示HashSet
 * @author 学霸联盟 - 赵灿
 */
public class HashSetDemo {
	public static void main(String[] args) {
		//创建HashSet对象
		HashSet hs = new HashSet();
		//循环向集合hs中添加元素
		for (int i = 0; i < 10; i++) {
			//创建Person对象
			Person p = new Person();
			//为Person对象的name属性赋值
			//有此处可知,加入的顺序是赵0、赵1、赵2......赵9
			p.name = "赵" + i;
			//将Person对象p加入集合
			hs.add(p);
		}
		/*
		 * Iterator(迭代器):提供一种访问Collection集合中每个元素的方法
		 * Iterator是一个接口
		 * 其中定义了hasNext()方法,用于判断集合中是否含有下一个元素
		 * 如果还有下一个元素,返回true;反之,返回false
		 * 还定义了next()方法,获取下一个元素
		 * Collection中定义了一个iterator()方法,用于获取Iterator对象
		 * 
		 * 由于Set集合无法使用下标访问元素,无法使用下标的方式遍历集合元素
		 * 所以java为我们提供了Iterator用于访问集合中的各个元素
		 */
		Iterator it = hs.iterator();
		//调用hasNext方法判断集合中是否还有下一个元素
		while(it.hasNext()){
			//调用next方法获取下一个元素
			Object obj = it.next();
			//强制类型转换
			Person p = (Person)obj;
			//输出name属性值,和加入的顺序不同
			System.out.print(p.name + "  ");
		}
	}
}
//Person类
class Person{
	public String name;
}
我的机器上运行的结果:
赵4  赵7  赵8  赵0  赵6  赵1  赵2  赵3  赵5  赵9

      从上面的结果可以看到,输出的顺序并不是加入时的顺序(未必一次就能运行出乱序的效果,可以瞬间连续多次点击eclipse上的运行按钮,这样看到乱序的效果几率比较大);所以称Set是无序的集合。
      那么Set是如何同时实现两种List集合的优点,又为什么没有顺序呢?
      首先Set同时使用了两种List集合的结构,即数组 + 链表(Set中使用的是单项链表:一个节点中只存储下一个节点,而不存储上一个节点);其中数组中的每一个位置均用于存储一个链表第一个元素。
      这里不得不提到hashCode和equals。在创建Set集合的子类对象是,会创建一个Node[] table(节点数组)用于存储Node对象,Node中存储下一个Node和加入集合的元素。
再向集合中添加元素A时,Set会创建一个Node对象nodeA(Node nodeA = new Node()),将元素A赋值给nodeA中key属性(nodeA.key = A),然后根据元素A的hashCode值计算出下标(假如得到的下标是1),最后将nodeA的地址赋值到下标为1的位置中。
      再次向集合中添加元素B时,又会创建Node对象nodeB,使用元素B的hashCode值计算出的下标也是1;此时会用equals方法判断元素A和元素B这两个对象是否相同。
      如果equals返回true,则表示A和B内容相同,此时不会再将nodeB加入集合。
      如果equals方法返回false,说明元素A和元素B是不同的两个对象,此时会将nodeB赋值给nodeA中的next属性(nodeA.next = nodeB),这样就形成了单项链表结构。
      由以上描述可知,Set中不能添加重复的元素,而且无法从Set中快速获取某一个指定的元素,只能使用Iterator为我们提供的遍历集合的方法获取Set中的每一个元素。
示例图


上述中创建的table称作散列表或哈希表,默认初始长度为16(如果创建集合对象时自定义初始容量,自定义的容量值必须是2的整倍数),加载因子默认为0.75(当集合中添加的元素个数等于当前长度乘以加载因子时,集合会自动将容量增加至原来的2倍)。

拓展阅读:《I学霸官方免费教程三十七:Java数据结构之单向链表结构》

在使用key的hashCode值计算下标(计算方法:int h = key.hashCode(); int hash = h^(h>>>16); int index = (table.length - 1) & hash),这样可以保证不越界,但无法保证顺序,所以Set集合是无序的。
这样做带来的优点是当加入和删除元素时,都无需将前后元素在数组中位移,只需根据计算出的下标和equals方法得到的结果,进行加入和删除即可。
缺点是没有顺序,无法快速获取某个元素,必须采用遍历的方式获取Set集合中的元素。

另外,可以利用重写Object类中的hashCode方法,来控制什么样的对象可以生成一致的(节点对象在hash表中的)下标。此时再利用重写Object类的equals方法,来控制对象是否属于重复的对象。而且必须两个方法得到的结果同时满足要求时,才认为两个对象时同一对象;任一方法不满足要求都认为是不同的对象。

实例:
package collection.set.hashSet;
import java.util.HashSet;
import java.util.Set;
/**
 * 演示重写hashCode方法和equals方法
 * 
 * @author 学霸联盟 - 赵灿
 */
public class HashCodeEqualsDemo {
	public static void main(String[] args) {
		/*
		 * 在堆内存中创建了一个PersonHE类型的对象
		 * 对象中保存的是"张三"和"123456789"两个字符串
		 */
		PersonHE p1 = new PersonHE("张三", "123456789", 10);
		/*
		 * 又在堆内存中创建了一个PersonHE类型的对象
		 * 对象中保存的也是"张三"和"123456789"两个字符串
		 */
		PersonHE p2 = new PersonHE("张三", "123456789", 20);
		/*
		 * 此时在内存中有两个PersonHE类型的对象
		 * 如果PersonHE类没有重写父类Object的equals方法
		 * 那么使用的则是Object类中的equals方法比较
		 * 和使用==比较一样,结果是:false
		 */
		System.out.println(p1 == p2);
		/*
		 * 现在已经重写了equals方法,按照姓名和身份证号比较
		 * 创建的时候姓名和身份证号赋的值都一样
		 * 所以此时使用equals比较得到的结果是:true
		 */
		System.out.println(p1.equals(p2));
		//创建HashSet对象(父类引用指向子类对象)
		Set setHE = new HashSet();
		//向集合中添加元素
		setHE.add(p1);
		/*
		 * PersonHE重写了hashCode方法和equals方法
		 * 使得对象p1和p2的hashCode相等,equals的结果为true
		 * 所以不会将对象p2加入集合
		 */
		setHE.add(p2);
		/*
		 * 可以使用Iterator + while循环获取Set集合中的元素
		 * 也可以使用增强for循环
		 * 语法格式:for(数据类型  变量名 : 继承Collection接口的集合或数组){}
		 * 作用:循环一次从集合或数组中取出一个元素赋值变量
		 * 注意:声明变量的数据类型,必须能够接收集合或数组中取出的元素的类型
		 * 反例:String[] str = {"abc", "xyz"};
		 * 		for(int i : str){};
		 * 		执行第一次循环,取出字符串abc,赋值给int类型的变量i
		 * 		这很明显是错误的
		 */
		for(Object obj:setHE){
			PersonHE pHE = (PersonHE)obj;
			/*
			 * 由于加入前一个对象p1被覆盖
			 * 所以这里只会输出一次,而且输出的是对象p1
			 * 输出结果:姓名:张三  身份证号:123456789  年龄:10
			 */
			System.out.println(pHE);
		}
	}
}


/**
 * 创建人类PersonHE
 * @author 学霸联盟 - 赵灿
 */
class PersonHE{
	//姓名
	private String name;
	//身份证号
	private String id;
	//年龄
	public int age;
	//声明带参构造方法
	public PersonHE(String name, String id, int age){
		this.name = name;
		this.id = id;
		this.age = age;
	}
	/**
	 * 重写父类的hashCode
	 * 目的:自定义计算hash值的算法
	 */
	@Override
	public int hashCode() {
		/*
		 * 这里自定义的计算规则是,姓名的hash值亦或身份证号的hash值
		 * 当然算法不是固定的,只要能符合需求即可
		 * 使用亦或运算只是为了尽可能少的出现相同情况
		 */
		return name.hashCode() ^ id.hashCode();
	}
	/**
	 * 重写父类的equals方法
	 * 目的:自定义比较两个对象内容是否相同的规则
	 */
	@Override
	public boolean equals(Object obj) {
		//将Object强制类型转换为Person
		PersonHE p = (PersonHE)obj;
		//这里自定义的比较规则是,当姓名和身份证号相同时,就表示是同一个人
		boolean result = this.name.equals(p.name) && this.id.equals(p.id);
		return result;
	}
	/**
	 * 重写父类的toString方法
	 * 目的:自定义将对象转换成字符串的规则
	 */
	@Override
	public String toString() {
		//这里自定义的规则是 ,返回姓名和身份证号的字符串
		return "姓名:" + name + "  身份证号:" + id + "  年龄:" + age;
	}
}
运行结果:
false
true
姓名:张三  省份证号:123456789  年龄:10

 

总结:

Set集合是无序的,不可重复的集合,采用散列(Hash)存储(数组 + 单向链表),遍历时可以采用Iterator + 循环和增强for循环,Iterator(迭代器)主要方法:hasNext()和next(),判断加入集合中的两个对象是否重复:
1、判断hashCode是否相同;
当hashCode相同,但equals比较结果为false时,两个对象会被保存在一个单向链表上,这个单向链表称为“桶”
2、使用equals判断内容是否相同;
当equals比较结果为true,但hashCode不同时,两个对象会被保存在不同的“桶”中

TreeSet类

既然Set集合是无序的,那么可不可以另外给Set增加排序的方法呢?答案是肯定的。
java为我们提供了TreeSet类,实现了自动对加入Set集合的元素进行排序。但是要求加入到TreeSet集合的元素类型必须实现Comparable接口,并实现接口中的compareTo()方法,在该方法中自定义排序方式。

实例:
package collection.set.treeSet;
import java.util.TreeSet;
/**
 * 演示泛型TreeSet集合
 * @author 学霸联盟 - 赵灿
 */
public class TreeSetDemo {
	public static void main(String[] args) {
		//创建三个树枝对象
		Branch b1 = new Branch("树枝10", 10);
		Branch b2 = new Branch("树枝5", 5);
		Branch b3 = new Branch("树枝8", 8);
		//创建TreeSet集合对象
		TreeSet ts = new TreeSet();
		//向集合中添加元素;加入TreeSet集合的元素类型必须实现Comparable接口
		ts.add(b1);
		ts.add(b2);
		ts.add(b3);
		//增强for循环遍历集合ts
		for (Object obj : ts) {
			//强制类型转换
			Branch b = (Branch) obj;
			//输出
			System.out.println("名称:" + b.getName() + "  年轮" + b.getAnnualRing());
		}
	}
}

/**
 * 树枝类Branch
 * 加入TreeSet集合的元素类型必须实现Comparable接口
 * 否则程序运行的时候会出现异常
 * @author 学霸联盟 - 赵灿
 */
class Branch implements Comparable{
	//标识名称
	private String name;
	//年轮:用于排序的依据
	private int annualRing;
	//带参构造方法
	public Branch(String name, int annualRing){
		this.name = name;
		this.annualRing = annualRing;
	}
	//重写接口中的方法
	@Override
	public int compareTo(Object o) {
		//强制类型转换
		Branch b = (Branch)o;
		/*
		 * 使用年轮作为排序比较的依据
		 * 当前对象年轮减去参数传入的Branch对象的年龄
		 * 结果等于0表示:两个树枝年轮相等,排序不分先后
		 * 结果大于0表示:当前对象的年轮大,排在后面
		 * 结果小于0表示:参数对象的年轮大,排在后面
		 */
		int result = this.annualRing - b.annualRing;
		return result;
	}
	//获取名称
	public String getName(){
		return name;
	}
	//获取年轮
	public int getAnnualRing(){
		return annualRing;
	}
}
运行结果:
名称:树枝5  年轮5
名称:树枝8  年轮8
名称:树枝10  年轮10

 

版权声明:本文为博主原创文章,未经博主允许不得转载。

 

转载于:https://www.cnblogs.com/ixueba/p/4725150.html

版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。
本文链接:https://blog.csdn.net/weixin_30384031/article/details/98820606

智能推荐

Eclipse中配置WebMagic(已配置好Maven)_使用eclipse搭建webmagic工程-程序员宅基地

文章浏览阅读364次。1.WebMagicWebMagic是一个简单灵活的Java爬虫框架。基于WebMagic,你可以快速开发出一个高效、易维护的爬虫。2.在Eclipse中配置WebMagic1.首先需要下载WebMagic的压缩包官网地址为:WebMagic官网最新版本为:WebMagic-0.7.3,找到对应版本,打开下载界面,注意,下载要选择Source code(zip)版本,随便下载到哪里都可以;2.下载好的压缩包需要解压,此时解压到的位置即为后续新建的Eclipse的project位置,比如我的Ecli_使用eclipse搭建webmagic工程

linux启动mysql_linux如何启动mysql服务_linux启动mysql服务命令是什么-系统城-程序员宅基地

文章浏览阅读1.9k次。mysql数据库是一种开放源代码的关系型数据库管理系统,有很多朋友都在使用。一些在linux系统上安装了mysql数据库的朋友,却不知道该如何对mysql数据库进行配置。那么linux该如何启动mysql服务呢?接下来小编就给大家带来linux启动mysql服务的命令教程。具体步骤如下:1、首先,我们需要修改mysql的配置文件,一般文件存放在/etc下面,文件名为my.cnf。2、对于mysql..._linux中 mysql 启动服务命令

php实现在线oj,详解OJ(Online Judge)中PHP代码的提交方法及要点-程序员宅基地

文章浏览阅读537次。详解OJ(Online Judge)中PHP代码的提交方法及要点Introduction of How to submit PHP code to Online Judge SystemsIntroduction of How to commit submission in PHP to Online Judge Systems在目前常用的在线oj中,codeforces、spoj、uva、zoj..._while(fscanf(stdin, "%d %d", $a, $b) == 2)

java快捷键调字体_设置MyEclipse编码、补全快捷键、字体大小-程序员宅基地

文章浏览阅读534次。一、设置MyEclipse编码(1)修改工作空间的编码方式:Window-->Preferences-->General-->Workspace-->Text file encoding(2)修改一类文件的编码方式:Window-->Preferences-->General-->content Types-->修改default Encoding(..._java修改快捷缩写内容

解析蓝牙原理_蓝牙原理图详解-程序员宅基地

文章浏览阅读1.4w次,点赞19次,收藏76次。1.前言市面上关于Android的技术书籍很多,几乎每本书也都会涉及到蓝牙开发,但均是上层应用级别的,而且篇幅也普遍短小。对于手机行业的开发者,要进行蓝牙模块的维护,就必须从Android系统底层,至少框架层开始,了解蓝牙的结构和代码实现原理。这方面的文档、网上的各个论坛的相关资料却少之又少。分析原因,大概因为虽然蓝牙协议是完整的,但是并没有具体的实现。蓝牙芯片公司只负责提供最底层的API_蓝牙原理图详解

从未在一起更让人遗憾_“从未在一起和最终没有在一起哪个更遗憾”-程序员宅基地

文章浏览阅读7.7k次。图/源于网络文/曲尚菇凉1.今天早上出门去逛街,在那家冰雪融城店里等待冰淇淋的时候,听到旁边两个女生在讨论很久之前的一期《奇葩说》。那期节目主持人给的辩论题是“从未在一起和最终没有在一起哪个更遗憾”,旁边其中一个女生说,她记得当时印象最深的是有个女孩子说了这样一句话。她说:“如果我喜欢一个人呢,我就从第一眼到最后一眼,把这个人爱够,把我的感觉用光,我只希望那些年让我成长的人是他,之后的那些年他喝过..._从未在一起更遗憾

随便推点

Spring Cloud Alibaba 介绍_sprngcloud alba-程序员宅基地

文章浏览阅读175次。Spring Cloud Alibaba 介绍Sping体系Spring 以 Bean(对象) 为中心,提供 IOC、AOP 等功能。Spring Boot 以 Application(应用) 为中心,提供自动配置、监控等功能。Spring Cloud 以 Service(服务) 为中心,提供服务的注册与发现、服务的调用与负载均衡等功能。Sping Cloud介绍官方介绍​ Tools for building common patterns in distributed systems_sprngcloud alba

测试 数据类型的一些测试点和经验_基础字段的测试点-程序员宅基地

文章浏览阅读3.2k次,点赞4次,收藏21次。我这里是根据之前在测试数据类项目过程中的一些总结经验和掉过个坑,记录一下,可以给其他人做个参考,没什么高深的东西,但是如果不注意这些细节点,后期也许会陷入无尽的扯皮当中。1 需求实现的准确度根据产品需求文档描述发现不明确不详细的或者存在歧义的地方一定要确认,例如数据表中的一些字段,与开发和产品确认一遍,如有第三方相关的,要和第三方确认,数据类项目需要的是细心,哪怕数据库中的一个字段如果没有提前对清楚,后期再重新补充,会投入更大的精力。2 数据的合理性根据业务场景/常识推理,提..._基础字段的测试点

一文看懂:行业分析怎么做?_码工小熊-程序员宅基地

文章浏览阅读491次。大家好,我是爱学习的小xiong熊妹。在工作和面试中,很多小伙伴会遇到“对XX行业进行分析”的要求。一听“行业分析”四个字,好多人会觉得特别高大上,不知道该怎么做。今天给大家一个懒人攻略,小伙伴们可以快速上手哦。一、什么是行业?在做数据分析的时候,“行业”两个字,一般指的是:围绕一个商品,从生产到销售相关的全部企业。以化妆品为例,站在消费者角度,就是简简单单的从商店里买了一支唇膏回去。可站在行业角度,从生产到销售,有相当多的企业在参与工作(如下图)在行业中,每个企业常常扮._码工小熊

LLaMA 简介:一个基础的、650 亿参数的大型语言模型_llma-程序员宅基地

文章浏览阅读1.6w次,点赞2次,收藏2次。还需要做更多的研究来解决大型语言模型中的偏见、有毒评论和幻觉的风险。我们在数万亿个令牌上训练我们的模型,并表明可以仅使用公开可用的数据集来训练最先进的模型,而无需诉诸专有和不可访问的数据集。在大型语言模型空间中训练像 LLaMA 这样的小型基础模型是可取的,因为它需要更少的计算能力和资源来测试新方法、验证他人的工作和探索新的用例。作为 Meta 对开放科学承诺的一部分,今天我们公开发布 LLaMA(大型语言模型元 AI),这是一种最先进的基础大型语言模型,旨在帮助研究人员推进他们在 AI 子领域的工作。_llma

强化学习在制造业领域的应用:智能制造的未来-程序员宅基地

文章浏览阅读223次,点赞3次,收藏5次。1.背景介绍制造业是国家经济发展的重要引擎,其产能和质量对于国家经济的稳定和发展具有重要意义。随着工业技术的不断发展,制造业的生产方式也不断发生变化。传统的制造业通常依赖于人工操作和手工艺,这种方式的缺点是低效率、低产量和不稳定的质量。随着信息化、智能化和网络化等新技术的出现,制造业开始向智能制造迈出了第一步。智能制造的核心是通过大数据、人工智能、计算机视觉等技术,实现制造过程的智能化、自动化...

ansible--安装与使用_pip安装ansible-程序员宅基地

文章浏览阅读938次。系列文章目录文章目录系列文章目录 前言 一、ansible是什么? 二、使用步骤 1.引入库 2.读入数据 总结前言菜鸟一只,刚开始使用,仅作以后参考使用。边学习,边记录,介绍一下最基础的使用,可能会有理解不到位的地方,可以共同交流,废话不多说,走起。一、ansible 简介?ansible是自动化运维工具的一种,基于Python开发,可以实现批量系统配置,批量程序部署,批量运行命令,ansible是基于模块工作的,它本身没有批量部署的能力,真正.._pip安装ansible

推荐文章

热门文章

相关标签