热门标签 | HotTags
当前位置:  开发笔记 > 编程语言 > 正文

9.Set集合

目录9.Set集合9.1.Set集合9.1.1.Set集合遍历方式9.1.2.Set集合特点9.1.3.Set没有对父接口Collection进行功能上扩充9.1.4.

目录

9.Set集合

9.1.Set集合

9.1.1.Set集合遍历方式

9.1.2.Set集合特点

9.1.3.Set没有对父接口Collection进行功能上扩充

9.1.4.Set存储元素为什么不重复

9.2.Set接口实现类_HashSet

9.2.1.HashSet存储结构

9.2.2.HashSet特点

9.2.3.HashSet没有对父接口Set进行功能上补充

9.2.4.HashSet存储自定义类型元素必须重写哪两个方法,为什么

9.3.哈希值HashCode

9.4.哈希表

9.4.1.jdk8前后哈希表区别

9.4.2.哈希表特点,为什么查询速度快(哈希表原理)

9.5.Set接口实现类_LinkedHashSet

9.5.1.LinkedHashSet存储结构、存储元素特点、与HashSet区别

 



 




9.Set集合




9.1.Set集合


9.1.1.Set集合遍历方式


9.1.2.Set集合特点


9.1.3.Set没有对父接口Collection进行功能上扩充


9.1.4.Set存储元素为什么不重复


1.java.util.Set接口和java.util.List接口一样,同样继承自Collection接口,它与Collection接口中方法基本一致。并没有对Collection接口进行功能上的扩充,只是比Collection接口更加严格。与List接口不同的是,Set接口中元素无序,并且都会以某种规则保证存入的元素不出现重复。
2.set集合有多个子类,主要介绍其中java.util.HashSet和java.utill.LinkedHashSet两个集合。
3.Set集合取出元素的方式迭代器,增强for;
4.Set接口特点
     1.不允许存储重复元素,子类集合既有有序也有无序的。
     2.没有索引,没有带索引的方法,也不能使用普通for循环遍历
5.java.util.Set接口 extends Collection接口
  Java.util.HashSet集合 implements Set接口
6.Set接口中的方法和Collection接口中方法一致,不再学习,下面主要学习Set接口的子类(实现类)



/*** Set集合不允许重复元素的原理:*/
public class Demo3_HashSet {public static void main(String[] args) {//创建HashSet集合对象HashSet set = new HashSet<>();String s1 = new String("abc");String s2 = new String("abc");set.add(s1);set.add(s2);set.add("重地");set.add("通话");set.add("abc");System.out.println(set);//不重复[重地, 通话, abc]}
}

1.当new HashSet<>()时在堆内存开辟空间创建集合,HashSet底层是哈希表:数组+链表/红黑树。哈希表横向是数组结构,竖向是链表/红黑树结构。
2.set集合在调用add方法存储元素时会调用元素的hashCode方法和equals方法判断元素是否重复set.add(s1):add方法会调用s1的hashCode方法,计算字符串"abc"的哈希值为96354,然后在集合中找有没有96354这个哈希值的元素,若没有则将s1存储到集合中。
3.set.add(s2):add方法会调用s2的hashCode方法,计算字符串"abc"的哈希值为96354,然后在集合中找有没有96354这个哈希值的元素,发现有(哈希冲突),s2会调用equals方法和哈希值相同的元素进行比较即s2.equals(s1),若两个元素的哈希值相同并且equals方法返回true,则认定这两个元素相同,就不会把s2存储到集合中。
4.set.add("重地"):add方法会调用“重地”的hashCode方法,计算字符串“重地”哈希值1179395,然后在集合中找有没有1179395这个哈希值元素,若没有就会把"重地"字符串存储到集合中。
5.set.add("通话"):add方法会调用“通话”的hashCode方法,计算字符串“通话”的哈希值为1179395,然后在集合中找有没有1179395这个哈希值的元素,发现有(哈希冲突),“通话”会调用equals方法和哈希值相同的元素进行比较即“通话”.equals("重地"),若两个元素的哈希值相同但equals方法返回 false,则认定这两个元素不相同,就会把"通话"存储到集合中。



9.2.Set接口实现类_HashSet


9.2.1.HashSet存储结构


9.2.2.HashSet特点


9.2.3.HashSet没有对父接口Set进行功能上补充


9.2.4.HashSet存储自定义类型元素必须重写哪两个方法,为什么


1.HashSet特点:
 *      1.存储不重复元素
 *      2.没有索引,没有带索引的方法,也不能使用普通的for循环遍历
 *      3.是一个无序的集合,存储元素和取出元素的顺序有可能不一致。
 *      4.底层是一个HashMap哈希表结构(特点是查询速度非常快)
 *      5.因为HashSet中没有除Set中方法外的特有方法,所以可以使用多态创建集合对象。因为多态弊端就是看不到子类的特有方法

public class Demo1_HashSet {public static void main(String[] args) {//使用多态创建HashSet集合对象Setset=new HashSet<>();//使用add方法向集合添加元素set.add(1);set.add(3);set.add(2);set.add(1);//使用迭代器(增强for)遍历set集合Iterator it = set.iterator();while(it.hasNext()){Integer next = it.next();System.out.println(next);//1,2,3(无序,不重复)}}
}

 

2.HashSet存储自定义类型元素:

给HashSet中存放自定义类型的元素时,必须重写对象中hashCode方法和equals方法,建立自己的比较方式才能保证HashSet集合中的对象唯一(元素不重复)。之前使用hashSet存储字符串,Integer等类型元素,他们都是java已经定义好的类,这些类都已经重写了hashCode和equals方法。因此我们使用hashSet存储自定义类型元素,必须重写这两个方法才能保证元素 的唯一性。

* HashSet存储自定义类型元素:* Set集合存储元素唯一(不重复)前提:必须重写hashCode方法和equals方法* 要求:同名同年龄的人,视为同一个人,只能存储一次。public class Demo4_HashSet {public static void main(String[] args) {//创建HashSet集合存储PersonHashSet set=new HashSet();Demo4_Person p1 = new Demo4_Person("zhangsan",18);Demo4_Person p2 = new Demo4_Person("zhangsan", 18);Demo4_Person p3 = new Demo4_Person("zhangsan", 19);//p1、p2两个对象哈希值不同System.out.println(p1.hashCode());//21685669System.out.println(p2.hashCode());//2133927002//p1、p2两个对象地址值不同(未重写的equals方法比较的是元素的地址,等价==运算符)System.out.println(p1==p2);System.out.println(p1.equals(p2));set.add(p1);set.add(p2);set.add(p3);System.out.println(set);/*显然在没有重写hashCode方法和equals方法时,p1,p2虽然内容重复但* 还是被写进set集合了,违反了set集合的唯一性。hashCode获取的是元素哈希值,未重写的equals比较的是地址而不是内容,这两个方法未重写导致比较出来的结果都是false, 所以当元素哈希值不同但内容相同时也会被存入集合。* [Demo4_Person{name=&#39;zhangsan&#39;, age=19},* Demo4_Person{name=&#39;zhangsan&#39;, age=18},* Demo4_Person{name=&#39;zhangsan&#39;, age=18}]*//*** 下面使用Demo4_Person1创建对象,该类重写了hashCode和equals方法*/HashSet set1 = new HashSet<>();Demo4_Person1 q1 = new Demo4_Person1("zhangsan",18);Demo4_Person1 q2 = new Demo4_Person1("zhangsan", 18);Demo4_Person1 q3 = new Demo4_Person1("zhangsan", 19);set1.add(q1); //调用add时会调用hashCode和equals方法进行比较是否重复set1.add(q2);set1.add(q3);System.out.println(q1.hashCode());System.out.println(q2.hashCode());System.out.println(q1==q2);//比较的是地址System.out.println(q1.equals(q2));//重写equals后比较内容System.out.println(set1);}
}

 



9.3.哈希值HashCode


 * 1.哈希值
 *     哈希值是一个十进制的整数,由系统随机给出(就是对象的地址值,是一个逻辑地址,是模拟出来得到的地址,不是数据实际存储的物理地址)。
 * 2.Object类中有一个方法hashCode()可以获取对象的哈希值
 *     int hashCode():返回该对象的哈希值
 *     hashCode()方法的源码:public native int hashCode(); native:代表该方法调用的是本地操作系统的方法。
 



9.4.哈希表


9.4.1.jdk8前后哈希表区别


9.4.2.哈希表特点,为什么查询速度快(哈希表原理)


1.HashSet集合存储数据的结构是:哈希表
2.jdk1.8版本之前与之后,哈希表的区别(或jdk1.8与jdk1.9中哈希表区别)
        1.8版本之前:哈希表=数组+链表
        1.8版本之后:哈希表=数组+链表+红黑树(提高查询速度,折半查找,一次排除一半)
3.哈希表特点:查询速度快
4.哈希表的原理:
      1.数组结构:把元素进行了分组,相同哈希值的元素是一组
      2.链表/红黑树结构:把相同哈希值的元素连接到一起
      3.数组结构初始容量是16,即0-15,当向集合中存储数据时,先计算元素的哈希值(即在数组中存储的位置),比如元素“abc”的哈希值96354,即为这个元素在数组中存储位置。将哈希值存储在数组中,将元素挂到这个索引的下面。元素"重地"的哈希值和"通话"的哈希值都是1179395,称为哈希冲突,将这两个元素都挂到数组中值为1179395的索引下面。这就是为什么哈希表查询非常快,它先是把数据根据哈希值相同进行分组,然后通过链表或红黑树将相同哈希值的元素连接到一起。
      4.jdk1.9开始,当链表中的节点元素超过8个,就会将链表结构转化为红黑树结构(红黑树查询非常快)目的是提高查询速度。



9.5.Set接口实现类_LinkedHashSet


9.5.1.LinkedHashSet存储结构、存储元素特点、与HashSet区别


1.在java.util包中找到set接口,set接口的一个实现类HashSet类下有一个子类LinkedHashSet,也就是LinkedHashSet继承了HashSet并实现了Set接口
2.java.util.LinkedHashSet集合 extends HashSet集合
3.LinkedHashSet集合特点:
 *    底层是一个哈希表(数组+链表/红黑树)+链表多了一条链表(记录元素的存储顺序),保证元素有序.因此它与HashSet集合的区别就在于前者是有序的,后者是无序的,
 *    其它特点相同即元素不能重复元素没有索引

public class Demo5_LinkedHashSet {public static void main(String[] args) {HashSet set = new HashSet<>();set.add("www");set.add("abc");set.add("abc");set.add("itcast");System.out.println(set);//[abc, www, itcast]无序,不允许重复LinkedHashSet linked = new LinkedHashSet<>();linked.add("www");linked.add("abc");linked.add("abc");linked.add("itcast");System.out.println(linked);//[www, abc, itcast]有序,不允许重复}
}


9.6.TreeSet

HashSet是散列存储,TreeSet是二叉树有序存储,这个有序不是存储有序而是根据排序规则进行排序,自定义元素需要实现comparable接口重写compartor方法定义自己排序规则。


排序说明

如果现在要是想进行排序的话,则必须在 Person 类中实现 Comparable 接口。

public class Person implements Comparable

{private String name;private int age;public int compareTo(Person per) {if (this.age > per.age) {return 1;} else if (this.age }public class TreeSetPersonDemo01 {public static void main(String[] args) {Set

all = new TreeSet

();all.add(new Person("张三", 10));all.add(new Person("李四", 10));all.add(new Person("王五", 11));all.add(new Person("赵六", 12));all.add(new Person("孙七", 13));System.out.println(all);}
}

  • [姓名:张三,年龄:10, 姓名:王五,年龄:11, 姓名:赵六,年龄:12, 姓名:孙七,年龄:13]
  • 从以上的结果中可以发现,李四没有了。因为李四的年龄和张三的年龄是一样的,所以会被认为是同一个对象。则 此时必须修改 Person 类,如果假设年龄相等的话,按字符串进行排序。

public int compareTo(Person per) {if (this.age > per.age) {return 1;} else if (this.age }

小结


  • 关于 TreeSet 的排序实现,如果是集合中对象是自定义的或者说其他系统定义的类没有实现 Comparable 接口,则不能实现 TreeSet 的排序,会报类型转换(转向 Comparable 接口)错误。
  • 换句话说要添加到 TreeSet 集合中的对象的类型必须实现了 Comparable 接口。 不过 TreeSet 的集合因为借用了 Comparable 接口,同时可以去除重复值,而 HashSet 虽然是 Set 接口子类,但是对于没有复写 Object 的 equals 和 hashCode 方法的对象,加入了 HashSet 集合中也是不能去掉重复值的。

推荐阅读
  • Iamtryingtomakeaclassthatwillreadatextfileofnamesintoanarray,thenreturnthatarra ... [详细]
  • Java容器中的compareto方法排序原理解析
    本文从源码解析Java容器中的compareto方法的排序原理,讲解了在使用数组存储数据时的限制以及存储效率的问题。同时提到了Redis的五大数据结构和list、set等知识点,回忆了作者大学时代的Java学习经历。文章以作者做的思维导图作为目录,展示了整个讲解过程。 ... [详细]
  • 本文讨论了一个关于cuowu类的问题,作者在使用cuowu类时遇到了错误提示和使用AdjustmentListener的问题。文章提供了16个解决方案,并给出了两个可能导致错误的原因。 ... [详细]
  • 如何自行分析定位SAP BSP错误
    The“BSPtag”Imentionedintheblogtitlemeansforexamplethetagchtmlb:configCelleratorbelowwhichi ... [详细]
  • Java太阳系小游戏分析和源码详解
    本文介绍了一个基于Java的太阳系小游戏的分析和源码详解。通过对面向对象的知识的学习和实践,作者实现了太阳系各行星绕太阳转的效果。文章详细介绍了游戏的设计思路和源码结构,包括工具类、常量、图片加载、面板等。通过这个小游戏的制作,读者可以巩固和应用所学的知识,如类的继承、方法的重载与重写、多态和封装等。 ... [详细]
  • 开发笔记:加密&json&StringIO模块&BytesIO模块
    篇首语:本文由编程笔记#小编为大家整理,主要介绍了加密&json&StringIO模块&BytesIO模块相关的知识,希望对你有一定的参考价值。一、加密加密 ... [详细]
  • 本文介绍了OC学习笔记中的@property和@synthesize,包括属性的定义和合成的使用方法。通过示例代码详细讲解了@property和@synthesize的作用和用法。 ... [详细]
  • JavaSE笔试题-接口、抽象类、多态等问题解答
    本文解答了JavaSE笔试题中关于接口、抽象类、多态等问题。包括Math类的取整数方法、接口是否可继承、抽象类是否可实现接口、抽象类是否可继承具体类、抽象类中是否可以有静态main方法等问题。同时介绍了面向对象的特征,以及Java中实现多态的机制。 ... [详细]
  • 本文详细介绍了Java中vector的使用方法和相关知识,包括vector类的功能、构造方法和使用注意事项。通过使用vector类,可以方便地实现动态数组的功能,并且可以随意插入不同类型的对象,进行查找、插入和删除操作。这篇文章对于需要频繁进行查找、插入和删除操作的情况下,使用vector类是一个很好的选择。 ... [详细]
  • Java学习笔记之面向对象编程(OOP)
    本文介绍了Java学习笔记中的面向对象编程(OOP)内容,包括OOP的三大特性(封装、继承、多态)和五大原则(单一职责原则、开放封闭原则、里式替换原则、依赖倒置原则)。通过学习OOP,可以提高代码复用性、拓展性和安全性。 ... [详细]
  • Java在运行已编译完成的类时,是通过java虚拟机来装载和执行的,java虚拟机通过操作系统命令JAVA_HOMEbinjava–option来启 ... [详细]
  • 向QTextEdit拖放文件的方法及实现步骤
    本文介绍了在使用QTextEdit时如何实现拖放文件的功能,包括相关的方法和实现步骤。通过重写dragEnterEvent和dropEvent函数,并结合QMimeData和QUrl等类,可以轻松实现向QTextEdit拖放文件的功能。详细的代码实现和说明可以参考本文提供的示例代码。 ... [详细]
  • 本文介绍了如何在给定的有序字符序列中插入新字符,并保持序列的有序性。通过示例代码演示了插入过程,以及插入后的字符序列。 ... [详细]
  • Spring特性实现接口多类的动态调用详解
    本文详细介绍了如何使用Spring特性实现接口多类的动态调用。通过对Spring IoC容器的基础类BeanFactory和ApplicationContext的介绍,以及getBeansOfType方法的应用,解决了在实际工作中遇到的接口及多个实现类的问题。同时,文章还提到了SPI使用的不便之处,并介绍了借助ApplicationContext实现需求的方法。阅读本文,你将了解到Spring特性的实现原理和实际应用方式。 ... [详细]
  • Android源码深入理解JNI技术的概述和应用
    本文介绍了Android源码中的JNI技术,包括概述和应用。JNI是Java Native Interface的缩写,是一种技术,可以实现Java程序调用Native语言写的函数,以及Native程序调用Java层的函数。在Android平台上,JNI充当了连接Java世界和Native世界的桥梁。本文通过分析Android源码中的相关文件和位置,深入探讨了JNI技术在Android开发中的重要性和应用场景。 ... [详细]
author-avatar
快乐每一天Everyone
这个家伙很懒,什么也没留下!
PHP1.CN | 中国最专业的PHP中文社区 | DevBox开发工具箱 | json解析格式化 |PHP资讯 | PHP教程 | 数据库技术 | 服务器技术 | 前端开发技术 | PHP框架 | 开发工具 | 在线工具
Copyright © 1998 - 2020 PHP1.CN. All Rights Reserved | 京公网安备 11010802041100号 | 京ICP备19059560号-4 | PHP1.CN 第一PHP社区 版权所有