在日常开发中,我们经常使用 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(); String last = sortedSet.last();
SortedSet<String> headSet = sortedSet.headSet("Cherry"); SortedSet<String> tailSet = sortedSet.tailSet("Banana"); SortedSet<String> subSet = sortedSet.subSet("Banana", "Date");
|
自然排序 vs Comparator
1 2 3 4 5 6 7 8 9 10 11 12 13
| SortedSet<Integer> naturalOrder = new TreeSet<>(); naturalOrder.add(3); naturalOrder.add(1); naturalOrder.add(2); System.out.println(naturalOrder);
SortedSet<Integer> customOrder = new TreeSet<>(Comparator.reverseOrder()); customOrder.add(3); customOrder.add(1); customOrder.add(2); System.out.println(customOrder);
|
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);
String firstKey = map.firstKey(); String lastKey = map.lastKey();
|
🔑 模式提炼:范围查询优化
当需要频繁执行范围查询(如"查找所有大于 X 小于 Y 的元素")时,使用 Sorted 集合可以避免全表扫描,通过红黑树的特性快速定位边界,时间复杂度从 O(n) 优化到 O(log n)。
Navigable 集合
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));
int lower = set.lower(5);
int floor = set.floor(5);
int ceiling = set.ceiling(5);
int higher = set.higher(5);
int first = set.pollFirst(); int last = set.pollLast();
NavigableSet<Integer> descending = set.descendingSet(); System.out.println(descending);
|
NavigableMap 示例
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"); Map.Entry<String, String> ceilingEntry = map.ceilingEntry("C1");
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()); System.out.println(minHeap.poll()); System.out.println(minHeap.poll());
System.out.println(minHeap);
PriorityQueue<Integer> maxHeap = new PriorityQueue<>(Comparator.reverseOrder()); maxHeap.offer(5); maxHeap.offer(1); maxHeap.offer(3); System.out.println(maxHeap.poll());
|
实际应用场景
Top-K 问题
1 2 3 4 5 6 7 8 9 10 11 12 13
| 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(); } } 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 + ")"); }
|
🔑 模式提炼:动态极值维护
当需要动态维护集合中的最小值或最大值,且元素会频繁插入和删除时,优先级堆提供了 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<Key, String> hashMap = new HashMap<>(); hashMap.put(key1, "Value1"); hashMap.put(key2, "Value2"); System.out.println(hashMap.size());
IdentityHashMap<Key, String> identityMap = new IdentityHashMap<>(); identityMap.put(key1, "Value1"); identityMap.put(key2, "Value2"); System.out.println(identityMap.size());
|
使用场景
对象拓扑保持
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)) { 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()); System.out.println(set.first()); System.out.println(set.last());
|
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")); queue.put(new DelayedElement(1000, "Task 2")); queue.put(new DelayedElement(2000, "Task 3"));
while (!queue.isEmpty()) { DelayedElement element = queue.take(); System.out.println("Processed: " + element + " at " + System.currentTimeMillis()); }
|
实际应用场景
缓存过期
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<>();
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<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);
EnumSet<Day> workDays = EnumSet.range(Day.MONDAY, Day.FRIDAY); workDays.add(Day.SATURDAY); workDays.remove(Day.SUNDAY);
|
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
| enum Color { RED, GREEN, BLUE, YELLOW }
EnumSet<Color> enumSet = EnumSet.of(Color.RED, Color.GREEN);
HashSet<Color> hashSet = new HashSet<>(); hashSet.add(Color.RED); hashSet.add(Color.GREEN);
EnumSet<Color> es = EnumSet.allOf(Color.class); HashSet<Color> hs = new HashSet<>(Arrays.asList(Color.values()));
|
🔑 模式提炼:枚举专用集合
当集合元素类型是枚举时,EnumSet/EnumMap 提供了比通用集合更好的性能和更紧凑的内存占用。适用于标志位集合、状态机、配置管理等场景。
WeakHashMap
WeakHashMap 是一个使用弱引用作为键的 HashMap。当键不再被外部引用时,它可以被垃圾回收器回收,对应的键值对也会自动从 Map 中移除。
弱引用机制
1 2 3 4 5 6 7 8 9 10 11 12 13 14
| HashMap<String, String> strongMap = new HashMap<>(); String key = new String("test"); strongMap.put(key, "value"); key = null;
WeakHashMap<String, String> weakMap = new WeakHashMap<>(); key = new String("test"); weakMap.put(key, "value"); key = null;
System.gc(); System.out.println(weakMap.size());
|
实际应用场景
缓存实现
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; }
|
注意事项
1 2 3 4 5 6 7 8 9 10
| WeakHashMap<String, String> map = new WeakHashMap<>(); map.put("test", "value"); System.gc(); System.out.println(map.size());
map.put(new String("test"), "value"); System.gc(); System.out.println(map.size());
|
🔑 模式提炼:自动清理缓存
当需要缓存数据但不希望缓存阻止对象被垃圾回收时,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<String, Integer> map = new LinkedHashMap<>(); map.put("A", 1); map.put("B", 2); map.put("C", 3);
for (Map.Entry<String, Integer> entry : map.entrySet()) { System.out.println(entry.getKey() + ": " + entry.getValue()); }
LinkedHashSet<String> set = new LinkedHashSet<>(); set.add("A"); set.add("B"); set.add("C"); System.out.println(set);
|
保持访问顺序(LRU 缓存)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20
| LinkedHashMap<String, Integer> lruCache = new LinkedHashMap<>(16, 0.75f, true) { @Override protected boolean removeEldestEntry(Map.Entry<String, Integer> eldest) { return size() > 3; } };
lruCache.put("A", 1); lruCache.put("B", 2); lruCache.put("C", 3);
lruCache.get("A");
lruCache.put("D", 4);
System.out.println(lruCache.keySet());
|
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"); cache.put("key4", "value4");
System.out.println(cache.keySet());
|
性能对比
| 特性 |
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 缓存、历史记录 |
掌握这些不常见的集合类,能够帮助你在特定场景下选择最合适的数据结构,编写出更高效、更优雅的代码。