Java 集合之 Set 集合

Set 集合通常不能记住元素的添加元素,Set 集合和Collection 基本相同,没有提供额外的方法。
Set 集合不允许包含重复元素,如果试图把两个相同的元素放进同一个 Set 集合,则添加操作失败。

本文内容包含:

  1. HashSet 类
  2. LinkedHashSet 类
  3. TreeSet 类
  4. EnumSet 类
  5. 各 Set 类的性能分析

1. HashSet 类

HashSet 按 hash 算法存储集合中的元素,因此具有良好的存取和查找性能。

HashSet 具有以下特点:
在这里插入图片描述
当向HashSet 集合存入一个元素时,HashSet 会调用该对象的 hashCode() 方法来得到该对象的 hashCode 值,然后根据该 hashCode 值决定该对象在 HashSet 中的存储位置。如果有两个元素通过equals() 方法返回 true ,但他们的hashCode() 方法返回值不相等,HashSet 将会把它们存储在不同的位置,依然可以添加成功。
下面三个类 A、B、C ,它们分别重写了 equals()、hashCode()两个方法的一个或全部。
在这里插入图片描述
注意:
在这里插入图片描述
在这里插入图片描述
重写 hashCode() 方法的基本规则:
在这里插入图片描述
重写 hashCode() 方法的一般步骤:

  1. 把对象内每个有意义的实例变量(即每个参与 equals() 方法比较标准的实例变量)计算出一个int 类型的 hashCode() 值,计算方式如下表所示:
    在这里插入图片描述

  2. 用第一步计算出来的多个 hashCode 值组合计算出一个 hashCode 值返回。例如

    return f1.hashCode() + (int) f2;
    为了避免直接相加产生偶然相等,可以通过为各实例变量的hashCode 值乘以任意一个质数后再相加,例如:
    return f1.hashCode() * 19 + (int)f2 * 31;

如果向HashSet 中添加一个可变对象后,后面程序修改了该可变对象的实例变量,则可能导致它与集合中的其他元素相同(即两个对象通过equals() 方法比较返回 true,两个对象的 hashCode 值也相等),这就有可能导致 HashSet 中包含两个相同的对象。下面程序演示了这种情况:
在这里插入图片描述在这里插入图片描述
在这里插入图片描述
因此,程序员在将可变对象添加到 hashSet 中之后,尽量不要去修改该集合元素中参与计算 hashCode()、equals() 的实例变量,否则会导致 HashSet 无法正确操作这些集合元素。
在这里插入图片描述

2. LinkedHashSet 类

LinkedHashSet 集合也是根据元素的 hashCode 值来决定元素的存储位置。但它使用链表维护元素的次序,这样使得元素看起来是以插入的顺序保存的,也就是说,当遍历LinkedHashSet 集合里的元素的时候,LinkedHashSet 将会按照元素的添加顺序来访问集合里的元素。

LinkedHashSet 需要维护元素的插入顺序,因此性能略低于hashSet 的性能,但在迭代访问 Set 里的全部元素时将有很好的性能,因为它以链表来维护内部顺序。
在这里插入图片描述

3. TreeSet 类

TreeSet 类是 SortedSet 接口的实现类,TreeSet 可以保证集合元素处于排序状态,此外,与HashSet 集合相比,TreeSet 类还额外提供了如下几个方法:
在这里插入图片描述
TreeSet 集合采用红黑树的数据结构来存储集合元素。
Tree 排序通过两种方法:自然排序,定制排序
默认情况下采用自然排序

  1. 自然排序
    TreeSet 会调用集合元素的compareTo(Object obj) 方法来比较元素之间的大小关系,然后将集合元素按升序排序,这种方式就是自然排序。
    Java 的 Comparable 接口:
    在这里插入图片描述
    java 的一些常用类已经实现了 Comparable 接口,并提供了比较大小的标准,下面是实现Comparable 接口的常用类:
    在这里插入图片描述
    如果试图把一个对象添加到 TreeSet 时,该对象必须实现 Comparable 接口,否则程序会抛出异常。

在这里插入图片描述
在这里插入图片描述
此外,大部分类在实现 compareTo(Object obj) 方法时,都需要将被比较对象 obj 强制类型转换为相同类型,因为只有相同类的两个实例才会比较大小。
当试图把一个对象添加到 TreeSet 集合时,TreeSet 会调用对象的 compareTo(Object obj) 方法 与集合中的其它元素进行比较,这就要求集合中的其它元素与该元素是同一个类的实例。否则会引发 ClassCastException 异常。
在这里插入图片描述
上面代码会出错
总结起来就是:如果希望TreeSet 能正常运行,TreeSet 只能添加同一种类型的元素。
对于TreeSet 集合而言,判断其是否相等是通过compareTo(Object obj) 方法是否返回0。
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
如果向 TreeSet 添加一个可变对象后,并且后面程序修改了该可变对象的实例变量,这将导致它与其他对象的大小顺序发生了变化,但TreeSet 不会再次调整它们的顺序,甚至可能导致 TreeSet 中保存的这两个对象通过 CompareTo 方法比较返回 0 。
在这里插入图片描述
在这里插入图片描述
上面程序中的 R 对象对应的类正常重写了 equals() 方法和 compareTo() 方法,这两个方法都以 R 对象的count 实例变量作为判断的依据。当程序执行 1 行代码时,看到程序输出的 Set 集合处于有序状态;因为 R 类是一个可变类,因此可以改变 R 对象的 count 实例变量的值,程序通过 粗体字代码行改变了该集合的第一个元素和最后一个元素的 count 变量的值。当程序执行 2 行代码输出时,将会看到集合处于无序状态,而且集合中包含重复元素。运行后看到如下结果:
在这里插入图片描述
一旦改变了TreeSet 集合里可变元素的实例变量,当再试图删除该对象时,TreeSet 也会删除失败(甚至集合中原有的、实例变量没被修改但与被修改后元素相等的元素也无法删除),所以在上面程序的3 行代码处,删除count 为 -2 的 R 对象时,没有任何元素被删除,程序执行 4 代码时,可以看到删除了 count 为 5 的R 对象,这表明 TreeSet 可以删除没有被修改的实例变量、且不与其他被修改实例变量的对象重复的对象。
在这里插入图片描述
2. 定制排序
TreeSet 的自然排序是根据集合元素的大小,TreeSet 将它们以升序排序,如果需要实现定制排序,可以通过Comparator 接口的帮助。需要在创建TreeSet 集合对象时,提供一个 Comparator 对象与该 TreeSet 集合关联,由该 Comparator 对象负责集合对象的排序逻辑。由于 Comparator 是一个函数式接口,因此可以使用 Lambda 表达式来代替 Comparator 对象。
在这里插入图片描述
上面程序中粗体字部分部分使用了目标类型为 Comparator 的 lambda 表达式,它负责 ts 集合的排序。所以当把M 对象添加到 ts 集合中时,无需 M 类实现Comparable 接口,因为此时 TreeSet 无需通过 M 对象本身来比较大小,而是与 TreeSet 关联的 Lambda 表达式来负责接口元素的顺序。运行程序,看到如下结果:
在这里插入图片描述
在这里插入图片描述

4. EnumSet 类

EnumSet 是一个专为枚举类设计的集合类,EnumSet 中的所有元素都必须是制度枚举类型的枚举类,该枚举类型在创建EnumSet 时显式或隐式地指定。EnumSet 的集合元素也是有序的,EnumSet 以枚举值在 Enum 类中的定义顺序来决定集合元素的顺序。
在这里插入图片描述
EnumSet 类没有暴露任何构造器来创建该类的实例,程序应通过它提供的类方法来创建EnumSet 对象。EnumSet 类它提供了如下常用的类方法来创建 EnumSet 对象。
在这里插入图片描述
下面程序示范了如何使用 EnumSet 来保存枚举类的多个枚举值。
在这里插入图片描述
在这里插入图片描述在这里插入图片描述

5. 各 Set 实现类的性能分析

在这里插入图片描述
在这里插入图片描述

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值