学习:Java中的Set集合框架

        Java 提供了多种集合类来处理数据,其中 Set 接口是一个重要的部分。它主要用于存储不重复的元素。本文将深入探讨 HashSet、LinkedHashSet 和 TreeSet 这三种实现,并详细解释它们的底层原理和使用场景。

HashSet

特性

- 无序:存储元素没有特定顺序。
- 不重复:不允许存储重复元素。
- 无索引:没有类似列表的索引功能。

底层数据结构

        HashSet 底层采用哈希表(HashMap)来存储数据。具体而言,在 JDK 8 之前,HashSet 的底层数据结构是数组和链表的组合;而在 JDK 8 及以后,随着链表长度的增加,链表会转换为红黑树来提高性能。

哈希值

在了解 HashSet 的存储原理之前,首先需要理解哈希值。

- 哈希值:对象的整数表现形式。
  1. 如果没有重写 hashCode 方法,不同对象计算出的哈希值是不同的。
  2. 如果已经重写 hashCode 方法,不同的对象只要属性值相同,计算出的哈希值就是一样的。
  3. 在小部分情况下,不同的属性值或者不同的地址值计算出来的哈希值也有可能一样(哈希碰撞)。

存储原理

1. 创建数组:初始化一个默认长度为 16 且加载因子为 0.75 的数组,数组名为 table。
2. 计算哈希值:根据元素的哈希值和数组长度计算出元素应存入的位置。
3. 检查位置:判断计算出的数组位置是否为 null,如果是则直接存入。
4. 处理冲突:如果位置不为 null,表示有元素存在,则调用 equals 方法比较属性值:
   - 如果相同,则不存入。
   - 如果不同,则存入数组,并形成链表。

        JDK 8 以前,新的元素插入数组,老的元素挂在新元素下面。而 JDK 8 以后,新的元素直接挂在老元素下面,当链表长度超过 8 且数组长度大于等于 64 时,链表会自动转换为红黑树。

常见问题

1. 为什么存和取的顺序不一样?
   因为 HashSet 底层使用哈希表来存储数据,哈希表并不保证顺序。
   
2. 为什么没有索引?
   HashSet 的设计是为了快速查找,而不是按序访问,因此没有索引。
   
3. 如何保证数据去重?
   通过 hashCode 和 equals 方法来判断元素是否重复,确保每个元素唯一。

LinkedHashSet

特性

- 有序:按照插入顺序存储元素。
- 不重复:不允许存储重复元素。
- 无索引:没有类似列表的索引功能。

底层数据结构

        LinkedHashSet 基于哈希表,同时使用双向链表来维护元素的插入顺序。这意味着它既具备 HashSet 的快速查找能力,又能保留元素的插入顺序。

TreeSet

特性

- 不重复:不允许存储重复元素。
- 无索引:没有类似列表的索引功能。
- 可排序:元素可以按照自然顺序(升序)或自定义比较器进行排序。

底层数据结构

        TreeSet 基于红黑树实现,红黑树是一种自平衡二叉搜索树,能够保证基本操作(如插入、删除、查找)的时间复杂度为 O(log n)。

排序方式

1. 自然排序:元素的类需要实现 Comparable 接口,并重写 compareTo 方法,指定排序规则。

@Override
//this:表示当前要添加的元素
//o:表示已经在红黑树存在的元素

//返回值:
//负数:表示当前要添加的元素是小的,存左边
//正数:表示当前要添加的元素是大的,存右边
//0 :表示当前要添加的元素已经存在,舍弃
public int compareTo(Student o) {
    System.out.println("--------------");
    System.out.println("this:" + this);
    System.out.println("o:" + o);
    //指定排序的规则
    //只看年龄,我想要按照年龄的升序进行排列
    return this.getAge() - o.getAge();
}

2. 比较器排序:创建 TreeSet 对象时,传入一个 Comparator 对象,指定自定义排序规则。

//排序规则:按照长度排序,如果一样长则按照首字母排序
//o1:表示当前要添加的元素
//o2:表示已经在红黑树存在的元素
//返回值规则是一样的
TreeSet<String> ts = new TreeSet<>((o1, o2)->{
        // 按照长度排序
        int i = o1.length() - o2.length();
        // 如果一样长则按照首字母排序
        i = i == 0 ? o1.compareTo(o2) : i;
        return i;
});

使用场景

1. 元素可重复:使用 ArrayList,基于数组实现。
2. 增删操作多于查询:使用 LinkedList,基于链表实现。
3. 元素去重:使用 HashSet,基于哈希表实现。
4. 去重且保留顺序:使用 LinkedHashSet,基于哈希表和双向链表实现。
5. 元素排序:使用 TreeSet,基于红黑树实现。

结论

        选择合适的集合类对于程序性能和功能的实现至关重要。HashSet 提供快速的元素查找和去重能力,LinkedHashSet 保留了插入顺序,TreeSet 提供了有序性和排序功能。理解每种集合的底层实现和适用场景,有助于编写高效且可靠的 Java 应用程序。

  • 23
    点赞
  • 12
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值