【从零开始学Java | 第二十五篇】Set集合

张开发
2026/5/19 2:16:18 15 分钟阅读
【从零开始学Java | 第二十五篇】Set集合
目录前言一、Set系列集合1.Set集合的特点2.Set集合的实现类二、HashSet1.哈希表和哈希值2.hashCode()方法3.HashSet底层原理4.案例三、LinkedHashSet1.底层原理2.LinkedHashSet是如何保证有序的3.案例总结前言在前面的章节中学习了List系列集合我们知道List系列集合添加的元素是有序、可重复、有索引的而今天要学习的Set系列集合与List系列集合添加的元素的特点恰恰相反是无序、不重复、无索引的。但在实际开发中我们经常会遇到这样的需求“名单里不能有重复的身份证号”、“抽奖名单里每个人只能中一次奖”。如果用 List 去做我们需要写繁琐的if(!list.contains(e))判断效率极低。因此我们应该使用Set系列集合一、Set系列集合1.Set集合的特点无序存取顺序不一致不重复可以去除重复无索引没有带索引的方法所以不能用普通for来循环遍历也不能通过索引来获取元素2.Set集合的实现类HashSet无序、不重复、无索引LinkedHashSet有序、不重复、无索引TreeSet可排序、不重复、无索引Set接口中的方法基本上与Collection的API一致。二、HashSet1.哈希表和哈希值HashSet集合底层采取哈希表存储数据。哈希表是一种对增删改查数据性能都较好的结构。哈希表的组成JDK8之前数组链表JDK8开始数组链表红黑树哈希值对象的整数表现形式。根据hashCode方法计算出来的int类型的整数。该方法定义在Object类中所有对象都可以调用默认使用地址值进行计算。一般情况下会重写hashCode方法利用对象内部的属性值计算哈希值。对象的哈希值的特点如果没有重写hashCode方法不同对象计算出的哈希值是不同的。如果已经重写hashCode方法不同的对象只要属性值相同计算出的哈希值就是一样的。在小部分情况下不同的属性值或者不同的地址值计算出来的哈希值也有可能是一样的。哈希碰撞为什么要转换为哈希值哈希表底层是由数组组成的如果我们要存储数据我们需要计算这个数据的哈希值根据哈希值选择应该存入的位置因此当我们存入的数据类型为引用数据类型时我们需要把具体的对象转换为整数才能匹配哈希表的存储格式。2.hashCode()方法①当没有重写hashCode()方法时public class Test { public static void main(String[] args) { Student s1 new Student(zhangsan, 23); Student s2 new Student(zhangsan, 23); System.out.println(s1.hashCode()); System.out.println(s2.hashCode()); } }尽管两个对象的属性值相同但所输出的两个哈希值是完全不同的②当重写了hashCode()方法时Idea提供了自动重写hashCode的快捷方式此时再运行上面那段代码所输出的哈希值是相同的。③哈希碰撞属性值不同得到的哈希值却相同。3.HashSet底层原理①创建一个默认长度为16默认加载因子为0.75的数组数组名为table②根据元素的哈希值根数组的长度计算出应该存入的位置③判断当前位置是否为null如果是null直接存入④如果位置不为null表示有元素则调用equals方法比较属性值⑤一样不存不一样存入数组形成链表JDK8以前新元素存入数组老元素挂在新元素下面JDK8以后新元素直接挂在老元素下面JDK8以前JDK8以后加载因子的作用也就是数组的占用率当table数组当中的16*0.7512个位置已经被占用后会对该数组进行两倍的扩容也就是扩容到16*232的长度。另一种情况当链表长度大于8而且数组长度大于等于64链表就会转换为红黑树来存储。4.案例创建多个学生对象要求学生对象的属性值相同时我们认为是同一个对象public class Test { public static void main(String[] args) { Student s1 new Student(zhangsan, 23); Student s2 new Student(zhangsan, 23); Student s3 new Student(lisi, 24); Student s4 new Student(wangwu, 25); HashSetStudent hs new HashSet(); System.out.println(hs.add(s1)); System.out.println(hs.add(s2)); System.out.println(hs.add(s3)); System.out.println(hs.add(s4)); System.out.println(hs); } }三、LinkedHashSet1.底层原理有序、不重复、无索引有序是指存取的顺序是一致的。原理底层数据结构依旧是哈希表只是每个元素又额外的多了一个双链表的机制记录存取的顺序。2.LinkedHashSet是如何保证有序的每存入一个节点都会和前一个节点互相记录地址值。在遍历时就和普通的HashSet不同了LinkedHashSet遍历方式是通过双向链表来遍历的通过第一个添加的元素也就是头节点依次向后遍历。3.案例public class Test { public static void main(String[] args) { Student s1 new Student(zhangsan, 23); Student s2 new Student(zhangsan, 23); Student s3 new Student(lisi, 24); Student s4 new Student(wangwu, 25); HashSetStudent hs new HashSet(); System.out.println(hs.add(s1)); System.out.println(hs.add(s2)); System.out.println(hs.add(s3)); System.out.println(hs.add(s4)); System.out.println(hs); LinkedHashSetStudent lhs new LinkedHashSet(); System.out.println(lhs.add(s1)); System.out.println(lhs.add(s2)); System.out.println(lhs.add(s3)); System.out.println(lhs.add(s4)); System.out.println(lhs); } }总结在开发中我们的选择逻辑应该是怎么样的默认首选HashSet它的增删查改效率是最高的。需要顺序选LinkedHashSet比如处理日志或展现用户操作轨迹。需要排序选TreeSet比如成绩榜单、按时间排序的消息。

更多文章