java学习--LinkedHashSet

一、LinkedHashSet 是什么?

LinkedHashSet 是 Java 集合框架中 java.util 包下的实现类,它继承自 HashSet ,同时实现了 Set 接口,底层基于 LinkedHashMap 实现(本质是「哈希表 + 双向链表」)。

可以把它理解为:

  • 拥有 HashSet 的高效查询、去重特性(基于哈希表);
  • 额外通过双向链表 维护元素的插入顺序,解决了 HashSet 无序的问题。

核心特点:

  • 有序:能精准保留元素的插入顺序(遍历顺序 = 插入顺序),但不支持按索引访问;
  • 不可重复 :和 HashSet 一样,通过 hashCode() + equals() 保证去重;
  • 允许 null 值:仅能有一个 null(因为不可重复);
  • 非线程安全 :多线程环境需用 Collections.synchronizedSet(new LinkedHashSet<>()) 包装;
  • 效率 :查询 / 增删效率略低于 HashSet(多了链表维护的开销),但远高于 TreeSet,理想时间复杂度仍为 O(1)

二、核心原理

LinkedHashSet 的底层结构是「哈希表(数组 + 链表 / 红黑树) + 双向链表」:

  1. 哈希表:负责保证元素不重复、高效查询(和 HashSet 逻辑一致);
  2. 双向链表:额外记录元素的插入顺序,遍历的时候按链表顺序输出,而非哈希表的随机顺序。

简单来说:LinkedHashSet 就是给 HashSet 加了一条 "记录插入顺序" 的双向链表,既保留了 HashSet 的高效,又解决了无序的问题。

三、常用操作示例

下面是 LinkedHashSet 的完整使用示例,代码可直接运行,对比 HashSet 能明显看出 "有序" 的特性:

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

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

        // 2. 添加元素(重复元素不会被插入,且保留插入顺序)
        lhs.add("西瓜");
        lhs.add("苹果");
        lhs.add("香蕉");
        lhs.add("苹果"); // 重复元素,不插入
        lhs.add(null);   // 允许一个 null
        System.out.println("初始集合:" + lhs); 
        // 输出:[西瓜, 苹果, 香蕉, null](严格按插入顺序,而非哈希随机顺序)

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

        // 4. 删除元素(删除后链表顺序仍保持)
        lhs.remove(null);
        lhs.remove("西瓜");
        System.out.println("删除后集合:" + lhs); 
        // 输出:[苹果, 香蕉](剩余元素仍按原插入顺序)

        // 5. 遍历(三种方式,均按插入顺序输出)
        // 方式1:增强 for 循环(最常用)
        System.out.println("增强for循环遍历:");
        for (String fruit : lhs) {
            System.out.println(fruit); // 苹果 → 香蕉
        }

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

        // 方式3:forEach 方法(Java 8+)
        System.out.println("forEach遍历:");
        lhs.forEach(System.out::println); // 苹果 → 香蕉

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

        // 7. 自定义类作为元素(需重写 hashCode + equals,和 HashSet 一致)
        LinkedHashSet<Book> bookSet = new LinkedHashSet<>();
        bookSet.add(new Book(1, "Java编程思想"));
        bookSet.add(new Book(2, "Effective Java"));
        bookSet.add(new Book(1, "Java编程思想")); // 重复,不插入
        System.out.println("书籍集合:" + bookSet.size()); // 2
    }

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

        public Book(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;
            Book other = (Book) obj;
            return id == other.id && (name == null ? other.name == null : name.equals(other.name));
        }

        // 重写 toString,方便打印
        @Override
        public String toString() {
            return "Book{id=" + id + ", name='" + name + "'}";
        }
    }
}

四、LinkedHashSet vs HashSet vs TreeSet(核心对比)

为了帮你理清三者的选择逻辑,这里整理了关键区别:

特性 LinkedHashSet HashSet TreeSet
底层结构 哈希表 + 双向链表 哈希表 红黑树
有序性 插入顺序 无序 自然顺序 / 自定义排序
去重规则 hashCode() + equals() hashCode() + equals() Comparable/Comparator
允许 null 是(仅一个) 是(仅一个)
时间复杂度 O (1)(略高于 HashSet) O (1)(最优) O(log n)
核心优势 有序 + 高效去重 极致高效去重 排序 + 去重
适用场景 需保留插入顺序的去重 无需有序的高效去重 需排序的去重

总结

  1. LinkedHashSet 继承自 HashSet,底层是「哈希表 + 双向链表」,核心特性是保留插入顺序、不可重复、高效查询
  2. 它的去重逻辑和 HashSet 完全一致,自定义类作为元素时必须重写 hashCode()equals()
  3. 选择建议:无需有序用 HashSet,需保留插入顺序用 LinkedHashSet,需排序用 TreeSet。
相关推荐
virus59452 小时前
悟空CRM mybatis-3.5.3-mapper.dtd错误解决方案
java·开发语言·mybatis
初次见面我叫泰隆2 小时前
Qt——3、常用控件
开发语言·qt·客户端
没差c3 小时前
springboot集成flyway
java·spring boot·后端
无小道3 小时前
Qt——QWidget
开发语言·qt
时艰.3 小时前
Java 并发编程之 CAS 与 Atomic 原子操作类
java·开发语言
梵刹古音4 小时前
【C语言】 函数基础与定义
c语言·开发语言·算法
编程彩机4 小时前
互联网大厂Java面试:从Java SE到大数据场景的技术深度解析
java·大数据·spring boot·面试·spark·java se·互联网大厂
笨蛋不要掉眼泪4 小时前
Spring Boot集成LangChain4j:与大模型对话的极速入门
java·人工智能·后端·spring·langchain
梵刹古音4 小时前
【C语言】 结构化编程与选择结构
c语言·开发语言·嵌入式
Yvonne爱编码4 小时前
JAVA数据结构 DAY3-List接口
java·开发语言·windows·python