在日常开发中,我们经常使用 ArrayList、HashMap、HashSet 等常见集合类。但 Java 集合框架中还隐藏着许多不常见但功能强大的集合类,它们在特定场景下能提供更优雅、高效的解决方案。

本文将详细介绍这些不常见集合类的用法、底层原理和实际应用场景,帮助你构建更完整的集合使用思维模式。

Sorted 集合

Sorted 集合基于红黑树实现,能够自动维护元素的排序状态。主要实现类是 TreeSet 和 TreeMap,它们分别实现了 SortedSet 和 SortedMap 接口。

核心特性

  • 自动排序:元素按照自然顺序或自定义 Comparator 排序
  • 范围查询:提供高效的子集/子映射获取方法
  • 时间复杂度:基本操作(添加、删除、查找)为 O(log n)

关键方法

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
SortedSet<String> sortedSet = new TreeSet<>();

// 添加元素
sortedSet.add("Apple");
sortedSet.add("Banana");
sortedSet.add("Cherry");
sortedSet.add("Date");

// 获取首尾元素
String first = sortedSet.first(); // "Apple"
String last = sortedSet.last(); // "Date"

// 获取子集(前包后不包)
SortedSet<String> headSet = sortedSet.headSet("Cherry"); // [Apple, Banana]
SortedSet<String> tailSet = sortedSet.tailSet("Banana"); // [Banana, Cherry, Date]
SortedSet<String> subSet = sortedSet.subSet("Banana", "Date"); // [Banana, Cherry]

自然排序 vs Comparator

1
2
3
4
5
6
7
8
9
10
11
12
13
// 自然排序(元素实现 Comparable)
SortedSet<Integer> naturalOrder = new TreeSet<>();
naturalOrder.add(3);
naturalOrder.add(1);
naturalOrder.add(2);
System.out.println(naturalOrder); // [1, 2, 3]

// 自定义排序(降序)
SortedSet<Integer> customOrder = new TreeSet<>(Comparator.reverseOrder());
customOrder.add(3);
customOrder.add(1);
customOrder.add(2);
System.out.println(customOrder); // [3, 2, 1]

TreeMap 示例

1
2
3
4
5
6
7
8
9
10
11
SortedMap<String, Integer> map = new TreeMap<>();
map.put("Alice", 25);
map.put("Bob", 30);
map.put("Charlie", 28);

// 获取键的范围
SortedMap<String, Integer> headMap = map.headMap("Charlie");
System.out.println(headMap); // {Alice=25, Bob=30}

String firstKey = map.firstKey(); // "Alice"
String lastKey = map.lastKey(); // "Charlie"

🔑 模式提炼:范围查询优化

当需要频繁执行范围查询(如"查找所有大于 X 小于 Y 的元素")时,使用 Sorted 集合可以避免全表扫描,通过红黑树的特性快速定位边界,时间复杂度从 O(n) 优化到 O(log n)。


Navigable 集合继承自 Sorted 集合,提供了更强大的导航功能,能够查找"最接近"的元素。TreeSet 和 TreeMap 同时实现了 NavigableSet 和 NavigableMap 接口。

核心方法

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
NavigableSet<Integer> set = new TreeSet<>();
set.addAll(Arrays.asList(1, 3, 5, 7, 9));

// lower: 严格小于
int lower = set.lower(5); // 3

// floor: 小于等于
int floor = set.floor(5); // 5

// ceiling: 大于等于
int ceiling = set.ceiling(5); // 5

// higher: 严格大于
int higher = set.higher(5); // 7

// 获取并移除首尾元素
int first = set.pollFirst(); // 1
int last = set.pollLast(); // 9

// 降序视图
NavigableSet<Integer> descending = set.descendingSet();
System.out.println(descending); // [7, 5, 3]
1
2
3
4
5
6
7
8
9
10
11
12
NavigableMap<String, String> map = new TreeMap<>();
map.put("A", "Alpha");
map.put("B", "Beta");
map.put("C", "Gamma");
map.put("D", "Delta");

// 查找最接近的键
Map.Entry<String, String> floorEntry = map.floorEntry("C1"); // C=Gamma
Map.Entry<String, String> ceilingEntry = map.ceilingEntry("C1"); // D=Delta

// 降序遍历
NavigableMap<String, String> descendingMap = map.descendingMap();

🔑 模式提炼:邻近查找模式

在需要查找"最接近目标值"的场景(如价格区间匹配、时间窗口查找)时,Navigable 集合的 lower/floor/ceiling/higher 方法提供了 O(log n) 的精确查找能力,避免了手动遍历和比较。


Priority 集合

PriorityQueue 是基于优先级堆的无界优先队列,不保证迭代顺序,但保证 poll() 操作返回最小元素。

底层结构

PriorityQueue 使用最小堆(二叉堆)实现:

  • 根节点是最小元素
  • 父节点小于等于子节点
  • 使用数组存储,索引 i 的左子节点为 2i+1,右子节点为 2i+2

基本用法

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
// 默认最小堆
PriorityQueue<Integer> minHeap = new PriorityQueue<>();
minHeap.offer(5);
minHeap.offer(1);
minHeap.offer(3);
minHeap.offer(2);

System.out.println(minHeap.poll()); // 1 (最小元素)
System.out.println(minHeap.poll()); // 2
System.out.println(minHeap.poll()); // 3

// 注意:迭代顺序不保证有序
System.out.println(minHeap); // 可能输出 [2, 5, 3] 等非有序结果

// 最大堆(使用自定义 Comparator)
PriorityQueue<Integer> maxHeap = new PriorityQueue<>(Comparator.reverseOrder());
maxHeap.offer(5);
maxHeap.offer(1);
maxHeap.offer(3);
System.out.println(maxHeap.poll()); // 5 (最大元素)

实际应用场景

Top-K 问题

1
2
3
4
5
6
7
8
9
10
11
12
13
// 找出前 K 大的元素
public static List<Integer> topK(int[] nums, int k) {
PriorityQueue<Integer> minHeap = new PriorityQueue<>();

for (int num : nums) {
minHeap.offer(num);
if (minHeap.size() > k) {
minHeap.poll(); // 移除最小的,保留 K 个最大的
}
}

return new ArrayList<>(minHeap);
}

任务调度

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
class Task implements Comparable<Task> {
String name;
int priority;

public Task(String name, int priority) {
this.name = name;
this.priority = priority;
}

@Override
public int compareTo(Task other) {
return Integer.compare(this.priority, other.priority);
}
}

PriorityQueue<Task> taskQueue = new PriorityQueue<>();
taskQueue.offer(new Task("Task A", 3));
taskQueue.offer(new Task("Task B", 1));
taskQueue.offer(new Task("Task C", 2));

while (!taskQueue.isEmpty()) {
Task task = taskQueue.poll();
System.out.println("Executing: " + task.name + " (priority: " + task.priority + ")");
}
// 输出:Task B (1), Task C (2), Task A (3)

🔑 模式提炼:动态极值维护

当需要动态维护集合中的最小值或最大值,且元素会频繁插入和删除时,优先级堆提供了 O(log n) 的插入和删除操作,比每次重新排序的 O(n log n) 更高效。适用于实时 Top-K、任务调度、事件驱动等场景。


Identity 集合

IdentityHashMap 是一个特殊的 Map 实现,它使用 == 而非 equals() 来比较键。这意味着只有两个对象的引用完全相同时,才被认为是同一个键。

与 HashMap 的区别

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
class Key {
String value;

public Key(String value) {
this.value = value;
}

@Override
public boolean equals(Object obj) {
if (this == obj) return true;
if (!(obj instanceof Key)) return false;
return value.equals(((Key) obj).value);
}

@Override
public int hashCode() {
return value.hashCode();
}
}

Key key1 = new Key("test");
Key key2 = new Key("test");

// HashMap 使用 equals() 比较
HashMap<Key, String> hashMap = new HashMap<>();
hashMap.put(key1, "Value1");
hashMap.put(key2, "Value2");
System.out.println(hashMap.size()); // 1 (key1.equals(key2) 返回 true)

// IdentityHashMap 使用 == 比较
IdentityHashMap<Key, String> identityMap = new IdentityHashMap<>();
identityMap.put(key1, "Value1");
identityMap.put(key2, "Value2");
System.out.println(identityMap.size()); // 2 (key1 != key2)

使用场景

对象拓扑保持

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
// 深拷贝时保持对象引用关系
public class DeepCopy {
private IdentityHashMap<Object, Object> copiedObjects = new IdentityHashMap<>();

public <T> T deepCopy(T original) {
if (original == null) return null;

// 检查是否已复制
T copied = (T) copiedObjects.get(original);
if (copied != null) {
return copied; // 返回同一个引用,保持拓扑结构
}

// 创建新对象并记录
T newObject = createInstance(original);
copiedObjects.put(original, newObject);

// 递归复制字段
copyFields(original, newObject);

return newObject;
}
}

序列化代理

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
// 避免循环引用导致的栈溢出
public class Serializer {
private IdentityHashMap<Object, Integer> objectIds = new IdentityHashMap<>();

public void serialize(Object obj) {
if (objectIds.containsKey(obj)) {
// 已序列化,输出引用 ID
System.out.println("ref:" + objectIds.get(obj));
return;
}

int id = objectIds.size();
objectIds.put(obj, id);
System.out.println("obj:" + id);

// 序列化字段
serializeFields(obj);
}
}

🔑 模式提炼:引用语义映射

当需要区分"值相同"和"引用相同"的场景时,IdentityHashMap 提供了基于对象身份而非语义等价的映射能力。适用于对象图遍历、深拷贝、序列化等需要保持对象引用关系的场景。


SkipList 集合

跳表(Skip List)是一种概率数据结构,通过多层索引实现快速查找。Java 提供了基于跳表的并发集合:ConcurrentSkipListSet 和 ConcurrentSkipListMap。

跳表原理

跳表通过在原始链表上建立多层索引,实现类似二分查找的效果:

1
2
3
Level 2:  1 -----------------> 9
Level 1: 1 ------> 5 ------> 9
Level 0: 1 -> 3 -> 5 -> 7 -> 9

查找 7 的过程:

  • Level 2: 1 < 7, 前进
  • Level 1: 5 < 7, 前进
  • Level 0: 5 < 7, 前进;7 == 7, 找到

ConcurrentSkipListSet

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
ConcurrentSkipListSet<Integer> set = new ConcurrentSkipListSet<>();

// 多线程环境下安全操作
ExecutorService executor = Executors.newFixedThreadPool(10);
for (int i = 0; i < 100; i++) {
final int num = i;
executor.submit(() -> set.add(num));
}

executor.shutdown();
executor.awaitTermination(1, TimeUnit.SECONDS);

System.out.println(set.size()); // 100
System.out.println(set.first()); // 0
System.out.println(set.last()); // 99

ConcurrentSkipListMap

1
2
3
4
5
6
7
8
9
ConcurrentSkipListMap<String, String> map = new ConcurrentSkipListMap<>();

map.put("A", "Alpha");
map.put("B", "Beta");
map.put("C", "Gamma");

// 并发安全的操作
String value = map.get("B");
Map.Entry<String, String> floorEntry = map.floorEntry("B1");

与 TreeMap 的对比

特性 TreeMap/TreeSet ConcurrentSkipListMap/ConcurrentSkipListSet
线程安全
并发性能 需要外部同步 无锁并发
底层结构 红黑树 跳表
时间复杂度 O(log n) O(log n)

🔑 模式提炼:并发有序集合

在高并发环境下需要有序集合时,ConcurrentSkipListSet/Map 提供了无锁的并发访问能力,避免了 TreeMap 在并发场景下的同步开销。适用于高并发的排行榜、实时统计、消息队列等场景。


DelayQueue

DelayQueue 是一个无界阻塞队列,只有当元素的延迟时间到期时才能从队列中获取。元素必须实现 Delayed 接口。

Delayed 接口

1
2
3
4
5
6
public interface Delayed extends Comparable<Delayed> {
/**
* 返回剩余延迟时间(纳秒)
*/
long getDelay(TimeUnit unit);
}

基本用法

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
class DelayedElement implements Delayed {
private long delayTime; // 到期时间(毫秒)
private String data;

public DelayedElement(long delay, String data) {
this.delayTime = System.currentTimeMillis() + delay;
this.data = data;
}

@Override
public long getDelay(TimeUnit unit) {
long diff = delayTime - System.currentTimeMillis();
return unit.convert(diff, TimeUnit.MILLISECONDS);
}

@Override
public int compareTo(Delayed other) {
return Long.compare(this.delayTime, ((DelayedElement) other).delayTime);
}

@Override
public String toString() {
return data;
}
}

DelayQueue<DelayedElement> queue = new DelayQueue<>();
queue.put(new DelayedElement(3000, "Task 1")); // 3秒后到期
queue.put(new DelayedElement(1000, "Task 2")); // 1秒后到期
queue.put(new DelayedElement(2000, "Task 3")); // 2秒后到期

// take() 会阻塞直到有元素到期
while (!queue.isEmpty()) {
DelayedElement element = queue.take();
System.out.println("Processed: " + element + " at " + System.currentTimeMillis());
}
// 输出顺序:Task 2 (1秒后), Task 3 (2秒后), Task 1 (3秒后)

实际应用场景

缓存过期

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
class CacheEntry implements Delayed {
private String key;
private Object value;
private long expireTime;

public CacheEntry(String key, Object value, long ttl) {
this.key = key;
this.value = value;
this.expireTime = System.currentTimeMillis() + ttl;
}

@Override
public long getDelay(TimeUnit unit) {
return unit.convert(expireTime - System.currentTimeMillis(), TimeUnit.MILLISECONDS);
}

@Override
public int compareTo(Delayed other) {
return Long.compare(this.expireTime, ((CacheEntry) other).expireTime);
}
}

public class ExpiringCache {
private DelayQueue<CacheEntry> expireQueue = new DelayQueue<>();
private Map<String, CacheEntry> cache = new ConcurrentHashMap<>();

public void put(String key, Object value, long ttl) {
CacheEntry entry = new CacheEntry(key, value, ttl);
cache.put(key, entry);
expireQueue.put(entry);
}

public Object get(String key) {
CacheEntry entry = cache.get(key);
return entry != null ? entry.value : null;
}

// 后台线程清理过期条目
public void startCleanup() {
new Thread(() -> {
while (true) {
try {
CacheEntry expired = expireQueue.take();
cache.remove(expired.key);
System.out.println("Expired: " + expired.key);
} catch (InterruptedException e) {
break;
}
}
}).start();
}
}

订单超时取消

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
class Order implements Delayed {
private String orderId;
private long createTime;
private long timeout; // 超时时间(毫秒)

public Order(String orderId, long timeout) {
this.orderId = orderId;
this.createTime = System.currentTimeMillis();
this.timeout = timeout;
}

@Override
public long getDelay(TimeUnit unit) {
long remaining = (createTime + timeout) - System.currentTimeMillis();
return unit.convert(remaining, TimeUnit.MILLISECONDS);
}

@Override
public int compareTo(Delayed other) {
return Long.compare(
this.createTime + this.timeout,
((Order) other).createTime + ((Order) other).timeout
);
}

public void cancel() {
System.out.println("Order cancelled: " + orderId);
// 取消订单逻辑
}
}

DelayQueue<Order> orderQueue = new DelayQueue<>();

// 创建订单,30分钟未支付自动取消
Order order = new Order("ORD-001", 30 * 60 * 1000);
orderQueue.put(order);

// 后台线程处理超时订单
new Thread(() -> {
while (true) {
try {
Order expiredOrder = orderQueue.take();
expiredOrder.cancel();
} catch (InterruptedException e) {
break;
}
}
}).start();

🔑 模式提炼:时间驱动的任务调度

当需要在指定时间点执行任务时,DelayQueue 提供了基于优先级的阻塞队列实现,避免了定时轮询的性能开销。适用于缓存过期、订单超时、定时任务等时间敏感场景。


EnumSet 和 EnumMap

EnumSet 和 EnumMap 是专为枚举类型设计的高性能集合,利用枚举的特性实现了极致的性能优化。

EnumSet

EnumSet 使用位向量(Bit Vector)存储,空间和时间效率极高:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
enum Day {
MONDAY, TUESDAY, WEDNESDAY, THURSDAY, FRIDAY, SATURDAY, SUNDAY
}

// 创建 EnumSet
EnumSet<Day> weekdays = EnumSet.of(Day.MONDAY, Day.TUESDAY, Day.WEDNESDAY,
Day.THURSDAY, Day.FRIDAY);
EnumSet<Day> weekend = EnumSet.of(Day.SATURDAY, Day.SUNDAY);

// 范围创建
EnumSet<Day> allDays = EnumSet.allOf(Day.class);
EnumSet<Day> noDays = EnumSet.noneOf(Day.class);

// 补集操作
EnumSet<Day> complement = EnumSet.complementOf(weekdays); // weekend

// 集合操作
EnumSet<Day> workDays = EnumSet.range(Day.MONDAY, Day.FRIDAY);
workDays.add(Day.SATURDAY); // false (已满)
workDays.remove(Day.SUNDAY); // true

EnumMap

EnumMap 使用数组存储,键必须是枚举类型:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
enum Operation {
ADD, SUBTRACT, MULTIPLY, DIVIDE
}

EnumMap<Operation, String> descriptions = new EnumMap<>(Operation.class);
descriptions.put(Operation.ADD, "加法");
descriptions.put(Operation.SUBTRACT, "减法");
descriptions.put(Operation.MULTIPLY, "乘法");
descriptions.put(Operation.DIVIDE, "除法");

// 遍历(保证按枚举声明顺序)
for (Map.Entry<Operation, String> entry : descriptions.entrySet()) {
System.out.println(entry.getKey() + ": " + entry.getValue());
}

性能优势

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
// EnumSet vs HashSet
enum Color { RED, GREEN, BLUE, YELLOW }

// EnumSet: 使用单个 long 值存储(64位可存储64个枚举)
EnumSet<Color> enumSet = EnumSet.of(Color.RED, Color.GREEN);
// 内部结构:long = 0b0011 (位向量)

// HashSet: 使用数组+链表存储
HashSet<Color> hashSet = new HashSet<>();
hashSet.add(Color.RED);
hashSet.add(Color.GREEN);
// 内部结构:Node[] 数组 + 链表节点

// 性能测试
EnumSet<Color> es = EnumSet.allOf(Color.class);
HashSet<Color> hs = new HashSet<>(Arrays.asList(Color.values()));

// EnumSet 的 contains 操作:位运算 O(1)
// HashSet 的 contains 操作:哈希计算 + 链表遍历 O(1) 平均

🔑 模式提炼:枚举专用集合

当集合元素类型是枚举时,EnumSet/EnumMap 提供了比通用集合更好的性能和更紧凑的内存占用。适用于标志位集合、状态机、配置管理等场景。


WeakHashMap

WeakHashMap 是一个使用弱引用作为键的 HashMap。当键不再被外部引用时,它可以被垃圾回收器回收,对应的键值对也会自动从 Map 中移除。

弱引用机制

1
2
3
4
5
6
7
8
9
10
11
12
13
14
// 普通 HashMap:键被强引用
HashMap<String, String> strongMap = new HashMap<>();
String key = new String("test");
strongMap.put(key, "value");
key = null; // key 仍被 Map 引用,不会被 GC

// WeakHashMap:键被弱引用
WeakHashMap<String, String> weakMap = new WeakHashMap<>();
key = new String("test");
weakMap.put(key, "value");
key = null; // key 不再被强引用,GC 时会被回收

System.gc(); // 触发 GC
System.out.println(weakMap.size()); // 可能输出 0

实际应用场景

缓存实现

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
public class WeakCache<K, V> {
private WeakHashMap<K, V> cache = new WeakHashMap<>();

public void put(K key, V value) {
cache.put(key, value);
}

public V get(K key) {
return cache.get(key);
}

public int size() {
return cache.size();
}
}

// 使用场景:缓存大对象,当内存不足时自动释放
WeakCache<String, byte[]> imageCache = new WeakCache<>();
imageCache.put("image1", loadLargeImage("image1.jpg"));
// 当内存紧张时,这些大对象会被自动回收

对象元数据关联

1
2
3
4
5
6
7
8
9
10
11
12
13
// 为对象附加元数据,不阻止对象被回收
WeakHashMap<Object, Map<String, Object>> metadataMap = new WeakHashMap<>();

public void attachMetadata(Object obj, String key, Object value) {
metadataMap.computeIfAbsent(obj, k -> new HashMap<>()).put(key, value);
}

public Object getMetadata(Object obj, String key) {
Map<String, Object> metadata = metadataMap.get(obj);
return metadata != null ? metadata.get(key) : null;
}

// 当 obj 不再被使用时,其元数据也会自动清理

注意事项

1
2
3
4
5
6
7
8
9
10
// 错误示例:使用字符串字面量作为键
WeakHashMap<String, String> map = new WeakHashMap<>();
map.put("test", "value"); // "test" 是字符串常量,不会被 GC
System.gc();
System.out.println(map.size()); // 仍然是 1

// 正确示例:使用新创建的对象作为键
map.put(new String("test"), "value");
System.gc();
System.out.println(map.size()); // 可能是 0

🔑 模式提炼:自动清理缓存

当需要缓存数据但不希望缓存阻止对象被垃圾回收时,WeakHashMap 提供了自动清理机制。适用于对象元数据、临时缓存、监听器注册等场景。


LinkedHashMap 和 LinkedHashSet

LinkedHashMap 和 LinkedHashSet 在 HashMap/HashSet 的基础上增加了顺序保持功能,可以按照插入顺序或访问顺序遍历元素。

保持插入顺序

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
// LinkedHashMap 默认按插入顺序遍历
LinkedHashMap<String, Integer> map = new LinkedHashMap<>();
map.put("A", 1);
map.put("B", 2);
map.put("C", 3);

// 遍历顺序:A -> B -> C
for (Map.Entry<String, Integer> entry : map.entrySet()) {
System.out.println(entry.getKey() + ": " + entry.getValue());
}

// LinkedHashSet 同理
LinkedHashSet<String> set = new LinkedHashSet<>();
set.add("A");
set.add("B");
set.add("C");
System.out.println(set); // [A, B, C]

保持访问顺序(LRU 缓存)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
// accessOrder=true:按访问顺序遍历
LinkedHashMap<String, Integer> lruCache = new LinkedHashMap<>(16, 0.75f, true) {
@Override
protected boolean removeEldestEntry(Map.Entry<String, Integer> eldest) {
return size() > 3; // 缓存大小限制为 3
}
};

lruCache.put("A", 1);
lruCache.put("B", 2);
lruCache.put("C", 3);

// 访问 "A",将其移到末尾
lruCache.get("A");

// 添加 "D",触发 eldest 移除("B" 是最久未访问的)
lruCache.put("D", 4);

// 当前顺序:C -> A -> D
System.out.println(lruCache.keySet()); // [C, A, D]

LRU 缓存实现

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
public class LRUCache<K, V> extends LinkedHashMap<K, V> {
private final int maxSize;

public LRUCache(int maxSize) {
super(16, 0.75f, true);
this.maxSize = maxSize;
}

@Override
protected boolean removeEldestEntry(Map.Entry<K, V> eldest) {
return size() > maxSize;
}
}

// 使用示例
LRUCache<String, String> cache = new LRUCache<>(3);
cache.put("key1", "value1");
cache.put("key2", "value2");
cache.put("key3", "value3");

cache.get("key1"); // 访问 key1
cache.put("key4", "value4"); // 添加 key4,移除 key2(最久未访问)

System.out.println(cache.keySet()); // [key3, key1, key4]

性能对比

特性 HashMap LinkedHashMap
插入/删除 O(1) O(1)
查找 O(1) O(1)
遍历顺序 无序 插入顺序/访问顺序
内存开销 较小 较大(维护双向链表)

🔑 模式提炼:有序集合与 LRU 缓存

当需要保持元素顺序或实现 LRU 缓存时,LinkedHashMap/LinkedHashSet 提供了比手动维护顺序更优雅的解决方案。适用于历史记录、最近访问列表、缓存淘汰等场景。


总结

本文介绍了 Java 集合框架中不常见但功能强大的集合类,它们在特定场景下能提供更优雅、高效的解决方案:

集合类 核心特性 典型场景
TreeSet/TreeMap 自动排序、范围查询 范围查询、排序存储
NavigableSet/Map 邻近查找 价格匹配、时间窗口
PriorityQueue 优先级堆 Top-K、任务调度
IdentityHashMap 引用语义 深拷贝、序列化
ConcurrentSkipListSet/Map 并发有序集合 高并发排行榜
DelayQueue 延迟队列 缓存过期、订单超时
EnumSet/EnumMap 枚举专用 标志位、状态机
WeakHashMap 弱引用 自动清理缓存
LinkedHashMap/HashSet 顺序保持 LRU 缓存、历史记录

掌握这些不常见的集合类,能够帮助你在特定场景下选择最合适的数据结构,编写出更高效、更优雅的代码。