# 本质剖析 为什么要使用HashSet

# 本质剖析 为什么要使用HashSet

单列集合 - HashSet

特点一:去重与遍历

  1. 支持数据去重,可以使用 迭代器foreach 遍历数据。

    两种遍历方式的比较
    1. 迭代器遍历

      • 通过调用 实现了 Iterable 接口的 Iterator iterator(); 方法, 从而获取迭代器对象,逐一访问元素。
      • 优点:支持在遍历过程中安全地删除元素,避免并发修改异常。
      • 适用场景:对集合进行删除操作时推荐使用。

      示例代码

      Iterator<String> ite = collection.iterator();
      while (ite.hasNext()) {
           String e = iterator.next();
           // 遍历或处理元素
           if (条件) {
                iterator.remove(); // 安全删除
           }
      }
      
    2. foreach 遍历

      • 简洁明了,foreach 直接访问集合中的每个元素。
      • 优点:代码更简洁,适合仅需要 读取 或 简单操作 元素的场景。
      • 限制:不支持在遍历过程中直接删除元素,会抛出 ConcurrentModificationException

      示例代码

      for (String e : collection) {
          // 遍历或处理元素
      }
      
    选择建议
    • 删除元素:使用 迭代器遍历

    • 仅访问或修改元素:使用 foreach 遍历,代码更简洁。

      • 底层基于 哈希表(数组 + 链表)的存储方式,增删查改效率高。
    foreach 和 iterator() 的区别

    foreach 的底层依赖 Iterator

    • foreach 循环是由 编译器生成代码 时 自动替换 为 iterator 的调用。这使得所有实现了 Iterable 接口的集合都可以使用 foreach

    虽然底层机制相同,但 foreach显式使用 iterator 在功能和使用场景上有所不同:

    特性 foreach iterator
    便捷性 代码更简洁,无需显式创建 Iterator 对象 必须手动创建 Iterator 并控制遍历过程
    删除元素 不支持在遍历过程中删除元素(会抛出 ConcurrentModificationException 通过iterator.remove() 可安全删除元素
    灵活性 仅适合读取和简单操作元素 更灵活,可在遍历时进行复杂的集合操作
    适用场景 适合只读操作,无需更改集合结构 适合需要动态修改集合的场景

特点二:定位查询

数据存储无序,但有规则
  • 无序性HashSet 存储过程中看似无序,是因为其底层并不对进行顺序维护,元素的位置是由 哈希算法 动态计算决定的。

  • 规则性

    尽管 HashSet 中的元素存储是无序的,但它依赖哈希表的机制,遵循以下规则:

    1. 元素的存储位置通过 哈希值(hashCode) 计算得出,决定它在哈希表中的索引。

      什么是哈希值?
      哈希值是由Java 中任意类的实例(如字符串、数字、自定义类等)的 hashCode() 方法生成的整数,用于快速定位数据在哈希表中的存储位置。

      • 意义

        • 快速检索:减少查找复杂度,从 O(n) 降至 O(1)。

          啥意思

          即将逐一遍历提升为直接定位。

    2. 同样的对象始终会生成 一致 的存储位置,因此尽管看似无序,但只要哈希值计算逻辑一致,其存储顺序也是可预测的

      • 什么是一致性?
        哈希表运作的基本规则之一, 即两个对象通过 equals() 方法比较相等时,哈希值也必须相同(通过 hashCode() 方法计算)。
哈希表的存储规则

索引唯一性

  • 在哈希表中,通过哈希函数计算得到的 哈希值 被放到一个索引位置。
  • 意味着
    每个索引位置在哈希表中是独立的,每个索引表示一个“桶”(bucket)。
    一个桶可以包含一个元素,也可以包含多个冲突元素(通过链表或红黑树)。

哈希表如何存储元素?

  • 哈希表的核心是“键值对”,严格来说,HashSet 并不是键值对结构,但其底层实现依赖于 HashMap,因此可以看作是通过键值对的方式间接实现的。它将键的哈希值转化为表的索引,用于存储对应的值:

    • 哈希值计算:对键调用 hashCode(),计算其哈希值。

    • 存储方式

      • 如果索引位置为空,则直接将元素存储到该索引的桶中。
      • 如果索引位置已有元素(哈希冲突),则将元素放入对应桶的链表或红黑树中。

      使用哈希表,用来存储人的名字和电话号码:

      ​ 键:名字 "zs"

      ​ 值:电话号码 "1234567890"

      1. 计算哈希值:
        "zs".hashCode() 结果为 2307740(假设)。
      2. 转化为索引:
        使用哈希表大小(假设为 100),计算索引:index = 2307740 % 100 = 40
      3. 存储值:
        在哈希表的索引 40 位置,存储 "zs" 和他的电话号码:
        table[40] = ("zs", "1234567890")

      结果:通过名字 "John",计算哈希值后找到索引 40,即可快速检索到对应的电话号码。

      自行拓展

      • 索引计算公式:通过哈希值和哈希表的 大小/长度 计算具体索引:
        index = hashCode % tableSize
确保每个索引 仅 对应一个桶
  • 逻辑唯一性:对应哈希值的一致性每个索引是唯一的,它对应唯一的桶,不会有两个索引指向同一个存储位置。
  • 冲突管理
    • 即使多个元素哈希值映射到同一个索引(即冲突),它们依然是存储在该索引唯一对应的桶中。
    • 在桶中,冲突元素可以通过链表或红黑树的形式分开存储,但这些冲突元素仍然属于同一个索引。
  • 冲突解决
    • 一致性原则:a.equals(b) == true,则 a.hashCode() == b.hashCode()

意义

  • 索引唯一性保证了哈希表结构的有序性和查找效率:

    • 快速定位:通过索引直接找到对应桶,而无需全表扫描。

    • 结构清晰:即使有冲突,冲突元素也明确归属于某个唯一的索引。

    • 避免混乱:不同索引不会共享存储区域,确保了数据存储的安全性和分区逻辑的清晰性

数据存储与访问过程
  1. 存储过程
    • 添加元素时,HashSet 首先调用 hashCode() 方法计算哈希值,通过哈希函数确定元素在哈希表中的位置。
    • 如果该位置没有其他元素,则直接存储;如果已有元素(哈希冲突),通过链表或红黑树解决冲突。
  2. 访问过程
    • 查询元素时,同样调用 hashCode() 方法计算哈希值,找到对应的存储位置。
    • 如果该位置有多个元素(由于哈希冲突),则依次通过 equals() 方法比较元素内容,找到目标元素。
哈希冲突与存储优化
  • 哈希冲突/挂载现象

    ​ 不同元素的哈希值 可能哈希值相同 从而导致争抢 同一索引位置,称为哈希冲突。

    • 例如:两个元素 ABhashCode() 结果相同,它们会存储在同一个索引处,形成链表或红黑树(挂载现象)。
  • 在 JDK8 中,为了对 HashSet 在出现哈希冲突 时, 提高性能优化:

    • 红黑树优化存储:当发生哈希冲突时,传统链地址法(链表)会逐渐失效,尤其在链表过长时,性能会退化为线性查找 O(n)。为了解决这一问题,JDK8 引入了红黑树优化

      具体优化机制:

      • 阈值条件:
        如果某个桶的链表长度超过 8(默认值),则会将链表转换为红黑树。

      • 转换过程:

        • 当插入的元素导致链表长度达到阈值,哈希表会自动将冲突的链表节点转换为红黑树。

        • 红黑树是一种自平衡二叉搜索树,可以将查找时间复杂度从 O(n) 降低到 O(log n)。

          啥意思

          快速锁定范围,就像查字典, 从首字母开头。

      • 还原机制:
        如果在后续操作中,红黑树中的节点数量减少到 6 以下,则会恢复为链表,减少空间开销。

      优势

      1. 提升查找性能:
        红黑树的 O(log n) 查找性能显著优于链表的 O(n)。

      2. 内存优化:
        小规模冲突时,链表仍然是更节省内存的选择,而红黑树仅在必要时启用,兼顾性能和空间。

如果这篇文章帮到你, 帮忙点个关注呗, 点赞或收藏也行鸭 ~ (。•ᴗ-)✧

在这里插入图片描述
^ '(இ﹏இ`。)

你可能感兴趣的:(深入浅出,聊点底层,高效学习,java,数据结构)