LC.99恢复二叉搜索树:中序遍历定位错误节点与Morris O(1)解法
2026/10/15 0:16:18
LinkedHashSet 是 Java 集合框架中java.util包下的实现类,它继承自 HashSet,同时实现了Set接口,底层基于LinkedHashMap实现(本质是「哈希表 + 双向链表」)。
可以把它理解为:
核心特点:
hashCode()+equals()保证去重;Collections.synchronizedSet(new LinkedHashSet<>())包装;O(1)。LinkedHashSet 的底层结构是「哈希表(数组 + 链表 / 红黑树) + 双向链表」:
简单来说: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 | HashSet | TreeSet |
|---|---|---|---|
| 底层结构 | 哈希表 + 双向链表 | 哈希表 | 红黑树 |
| 有序性 | 插入顺序 | 无序 | 自然顺序 / 自定义排序 |
| 去重规则 | hashCode() + equals() | hashCode() + equals() | Comparable/Comparator |
| 允许 null | 是(仅一个) | 是(仅一个) | 否 |
| 时间复杂度 | O (1)(略高于 HashSet) | O (1)(最优) | O(log n) |
| 核心优势 | 有序 + 高效去重 | 极致高效去重 | 排序 + 去重 |
| 适用场景 | 需保留插入顺序的去重 | 无需有序的高效去重 | 需排序的去重 |
hashCode()和equals()。