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。
相关推荐
炽烈小老头24 分钟前
【每天学习一点算法 2026/04/20】除自身以外数组的乘积
学习·算法
程序猿编码38 分钟前
给你的网络流量穿件“隐形衣“:手把手教你用对称加密打造透明安全隧道
linux·开发语言·网络·安全·linux内核
aq55356001 小时前
编程语言三巨头:汇编、C++与PHP大比拼
java·开发语言
aq55356002 小时前
PHP vs Python:30秒看懂核心区别
开发语言·python·php
我是无敌小恐龙2 小时前
Java SE 零基础入门Day01 超详细笔记(开发前言+环境搭建+基础语法)
java·开发语言·人工智能·opencv·spring·机器学习
破浪前行·吴2 小时前
数据结构概述
数据结构·学习
码云数智-大飞2 小时前
零基础微信小程序制作平台哪个好
开发语言
心态与习惯2 小时前
Julia 初探,及与 C++,Java,Python 的比较
java·c++·python·julia·比较
神仙别闹2 小时前
基于 MATLAB 实现的 DCT 域的信息隐藏
开发语言·matlab
一叶飘零_sweeeet2 小时前
优秀文章合集
java