java学习--HashSet

一、HashSet 是什么?

HashSet 是 Java 集合框架中 java.util 包下的实现类,它实现了 Set 接口,底层基于 HashMap 实现(可以理解为 HashSet 是 HashMap 的 "马甲",只使用了 HashMap 的 key 部分,value 是一个固定的空对象)。

核心特点:

  • 无序:存储的元素不会按插入顺序、大小顺序等排列(底层是哈希表,元素位置由哈希值决定)。
  • 不可重复 :不允许存储重复元素(通过 hashCode() + equals() 方法保证)。
  • 允许 null 值:但只能有一个 null(因为不可重复)。
  • 非线程安全 :多线程环境下直接使用会有并发问题,可通过 Collections.synchronizedSet(new HashSet<>()) 包装。
  • 查询 / 增删效率高 :理想情况下时间复杂度为 O(1)(哈希值不冲突时)。

二、核心原理(去重逻辑)

HashSet 判断两个元素是否重复的规则:

  1. 先调用元素的 hashCode() 方法,获取哈希值;
  2. 如果哈希值不同,直接判定为不同元素,存入集合;
  3. 如果哈希值相同,再调用 equals() 方法:
    • equals() 返回 true:判定为重复元素,不存入;
    • equals() 返回 false:判定为不同元素,存入(哈希冲突,会以链表 / 红黑树形式存储)。

注意:如果自定义类作为 HashSet 元素,必须重写 hashCode() 和 equals() 方法,否则无法正确去重(默认使用 Object 类的方法,按内存地址判断)。

三、常用操作示例

下面是 HashSet 核心操作的完整代码,可直接运行:

复制代码
import java.util.HashSet;
import java.util.Iterator;

public class HashSetDemo {
    public static void main(String[] args) {
        // 1. 创建 HashSet 对象
        HashSet<String> set = new HashSet<>();

        // 2. 添加元素(add)
        set.add("Apple");
        set.add("Banana");
        set.add("Orange");
        set.add("Apple"); // 重复元素,不会被添加
        set.add(null);    // 允许添加一个 null
        System.out.println("初始集合:" + set); // 输出无序,比如 [null, Apple, Banana, Orange]

        // 3. 判断元素是否存在(contains)
        boolean hasBanana = set.contains("Banana");
        System.out.println("是否包含Banana:" + hasBanana); // true

        // 4. 获取集合大小(size)
        System.out.println("集合大小:" + set.size()); // 4

        // 5. 删除元素(remove)
        set.remove("Orange");
        set.remove(null);
        System.out.println("删除后集合:" + set); // [Apple, Banana]

        // 6. 遍历 HashSet(三种方式)
        // 方式1:增强 for 循环(最常用)
        System.out.println("增强for循环遍历:");
        for (String fruit : set) {
            System.out.println(fruit);
        }

        // 方式2:迭代器
        System.out.println("迭代器遍历:");
        Iterator<String> iterator = set.iterator();
        while (iterator.hasNext()) {
            System.out.println(iterator.next());
        }

        // 方式3:forEach 方法(Java 8+)
        System.out.println("forEach遍历:");
        set.forEach(System.out::println);

        // 7. 清空集合(clear)
        set.clear();
        System.out.println("清空后是否为空:" + set.isEmpty()); // true

        // 8. 自定义类作为元素(需重写 hashCode 和 equals)
        HashSet<Student> studentSet = new HashSet<>();
        studentSet.add(new Student(1, "张三"));
        studentSet.add(new Student(1, "张三")); // 重复元素,不会添加
        studentSet.add(new Student(2, "李四"));
        System.out.println("学生集合大小:" + studentSet.size()); // 2
    }

    // 自定义学生类(重写 hashCode 和 equals 保证去重)
    static class Student {
        private int id;
        private String name;

        public Student(int id, String name) {
            this.id = id;
            this.name = name;
        }

        // 重写 hashCode:根据 id 和 name 生成哈希值
        @Override
        public int hashCode() {
            final int prime = 31;
            int result = 1;
            result = prime * result + id;
            result = prime * result + ((name == null) ? 0 : name.hashCode());
            return result;
        }

        // 重写 equals:id 和 name 都相同则判定为同一对象
        @Override
        public boolean equals(Object obj) {
            if (this == obj) return true;
            if (obj == null || getClass() != obj.getClass()) return false;
            Student other = (Student) obj;
            return id == other.id && (name == null ? other.name == null : name.equals(other.name));
        }
    }
}

四、HashSet vs TreeSet vs LinkedHashSet

特性 HashSet TreeSet LinkedHashSet
底层结构 哈希表(HashMap) 红黑树(TreeMap) 哈希表 + 双向链表
有序性 无序 自然顺序 / 自定义排序 插入顺序
去重规则 hashCode() + equals() 实现 Comparable/Comparator hashCode() + equals()
允许 null 是(仅一个) 否 是(仅一个)
效率 高(O (1)) 中(O (log n)) 高(略低于 HashSet)
适用场景 无需有序、高效去重 需要排序的去重场景 需要保留插入顺序的去重

总结

  1. HashSet 底层依赖 HashMap,核心特性是无序、不可重复、查询增删效率高 ,通过 hashCode() + equals() 保证去重。
  2. 自定义类作为 HashSet 元素时,必须重写 hashCode() 和 equals(),否则无法正确去重。
  3. 选择 Set 实现类的核心:无需有序用 HashSet,需排序用 TreeSet,需保留插入顺序用 LinkedHashSet。
相关推荐
Nebula_g2 分钟前
云禾外卖补充—项目整体后端运行逻辑-对流程的更深层次理解
java·开发语言·网络·数据库·状态模式·javase
萧瑟余晖3 分钟前
Spring Security 6.x新特性与架构演进详解
java·spring·架构
\光辉岁月/9 分钟前
1.Object类
java·面向对象·object类
想做小南娘,发现自己是女生喵12 分钟前
第二周学习文档 Linux C 文件操作
linux·c语言·学习
鸽鸽程序猿21 分钟前
【项目】【在线判题系统】公共功能
java·spring boot·maven
潜心一志26 分钟前
图像预处理—— 图像的变换与校正(理论)
学习·计算机视觉
nmjkhome28 分钟前
26 疲劳的几个重要概念(第51课)-solid works simulation
学习
一只笨菜鸟29 分钟前
IDEA如何让多个项目同时显示
java·ide·intellij-idea
程序猿乐锅32 分钟前
【从0-1学习Python | 第六篇】 Python 函数:定义、参数与返回值
开发语言·python·学习
燕落南枝33 分钟前
HTML 学习:input 单选、复选与文件域详解
服务器·学习·html