告别随机访问尴尬:Java 21 Sequenced Collections让有序集合操作开箱即用

2026-06-17 0 344

Java的集合框架一直有一个说大不大、说小不小的痛点:明明很多集合是有顺序的,但接口层面却不提供统一的方式来获取头尾元素、移除头尾元素、或者反向遍历。比如Listget(0)但没有直接取最后一个的方法,LinkedHashSet有顺序却没法快捷地拿到最后添加的元素,SortedSet/NavigableSet虽然提供了first()last(),但与列表完全不兼容。调用方为了获得末尾元素,往往需要根据不同的类型写出不同的代码,可读性大打折扣。

Java 21正式引入的Sequenced Collections彻底解决了这个问题。它为目标集合框架补充了一组关于“有序元素序列”的接口,让所有具有确定遍历顺序的集合都能以统一的方式操作序列的两端。

新接口总览

Sequenced Collections 主要在java.util包中增加了三个核心接口:

  • SequencedCollection<E>:扩展自Collection,提供了addFirstaddLastgetFirstgetLastremoveFirstremoveLast以及reversed()方法。
  • SequencedSet<E>:扩展自SetSequencedCollection,保持元素不重复,同时支持有序操作。
  • SequencedMap<K, V>:扩展自Map,提供putFirstputLastfirstEntrylastEntrypollFirstEntrypollLastEntry以及reversed()等方法。

现在,ListDequeLinkedHashSetSortedSetLinkedHashMapSortedMap等类都通过新接口获得了统一的两端访问能力。你不再需要记住哪个集合用哪个方法取头尾,直接选择最符合自然语言的getFirst()getLast()即可。

最简单的转变:统一获取首尾元素

假设你的程序里有一个Collection引用,它可能是一个ArrayList,也可能是LinkedHashSet。以前为了安全地取末尾元素,需要各种条件判断:

// 旧方式
if (collection instanceof List list) {
    return list.get(list.size() - 1);
} else if (collection instanceof SortedSet sortedSet) {
    return sortedSet.last();
} else {
    // 没有统一方式,只能遍历
}

现在,只要该集合实现了SequencedCollection,一行就够:

if (collection instanceof SequencedCollection seq) {
    return seq.getLast();
}

来看个实际例子,将多个不同的有序集合统一处理:

SequencedCollection list = new ArrayList(List.of("a", "b", "c"));
SequencedCollection linkedSet = new LinkedHashSet(List.of("x", "y", "z"));

System.out.println(list.getFirst());      // a
System.out.println(list.getLast());       // c
System.out.println(linkedSet.getFirst()); // x
System.out.println(linkedSet.getLast());  // z

反向视图:一行代码反转遍历

新接口提供的reversed()方法返回一个反向的视图,它不是复制整个集合,而是对原集合的一个倒序映射。任何在原集合上的修改都会立刻反映在反向视图里,反之亦然。

var original = new ArrayList(List.of(1, 2, 3));
var reversed = original.reversed();

System.out.println(original); // [1, 2, 3]
System.out.println(reversed); // [3, 2, 1]

original.add(4);
System.out.println(reversed); // [4, 3, 2, 1]

reversed.addFirst(0);
System.out.println(original); // [1, 2, 3, 4, 0]  注意:addFirst在反向视图上是添加在末尾

对于需要反向迭代的场景,使用增强for循环即可:

for (int num : original.reversed()) {
    System.out.println(num);
}

这比专门调用Collections.reverse()或者手动从listIterator(size)写起要简洁得多。

SequencedMap:让有序Map操作不再别扭

LinkedHashMap本身保留了插入顺序,但它的API一直缺少直接获取首尾条目的方法。通过SequencedMap,这些操作变得同样简单:

SequencedMap scores = new LinkedHashMap();
scores.put("Alice", 92);
scores.put("Bob", 85);
scores.put("Charlie", 78);

System.out.println(scores.firstEntry()); // Alice=92
System.out.println(scores.lastEntry());  // Charlie=78

// 移除并返回第一个条目
var first = scores.pollFirstEntry();
System.out.println(first); // Alice=92
System.out.println(scores); // {Bob=85, Charlie=78}

对于SortedMap(如TreeMap),SequencedMap同样适用,而且firstEntrylastEntry的逻辑和已有方法一致,但通过统一接口提高了可替换性。

实战案例:用SequencedMap实现一个简单的LRU缓存

我们借助LinkedHashMap的有序性和SequencedMap的新方法,快速实现一个线程安全的LRU缓存,当容量满时自动淘汰最近最少使用的条目。

import java.util.LinkedHashMap;
import java.util.SequencedMap;

public class LRUCache extends LinkedHashMap {
    private final int capacity;

    public LRUCache(int capacity) {
        super(capacity + 1, 0.75f, true); // access order
        this.capacity = capacity;
    }

    @Override
    protected boolean removeEldestEntry(Map.Entry eldest) {
        return size() > capacity;
    }

    // 通过SequencedMap获取最近最少使用的条目(即头元素)
    public Map.Entry getLRU() {
        return ((SequencedMap) this).firstEntry();
    }

    // 获取最近使用的条目(尾元素)
    public Map.Entry getMostRecent() {
        return ((SequencedMap) this).lastEntry();
    }

    public static void main(String[] args) {
        LRUCache cache = new LRUCache(3);
        cache.put("a", "apple");
        cache.put("b", "banana");
        cache.put("c", "cherry");
        System.out.println(cache);

        cache.get("a"); // 触发访问,a变为最近使用
        cache.put("d", "date"); // 容量满,淘汰最老的b

        System.out.println("LRU: " + cache.getLRU().getKey());     // c
        System.out.println("Most recent: " + cache.getMostRecent().getKey()); // d
    }
}

上面的代码利用LinkedHashMap的访问顺序模式,并直接调用SequencedMapfirstEntry()lastEntry()来观察缓存两端的元素。这种实现方式比单纯依赖keySet().iterator().next()更加语义化。

案例二:优先级任务队列中的头尾管理

再来看一个场景:你需要维护一个任务队列,支持正常按优先级出队,但也提供快速插入紧急任务到队首的能力。可以用LinkedHashSet保持插入顺序并配合SequencedSet

SequencedSet taskQueue = new LinkedHashSet();
taskQueue.add("发送日报");
taskQueue.add("备份数据库");
taskQueue.add("清理日志");

// 紧急任务插到最前
taskQueue.addFirst("修复线上故障");
System.out.println(taskQueue); // [修复线上故障, 发送日报, 备份数据库, 清理日志]

// 取出并移除最后一个(最不紧急)
String lastTask = taskQueue.removeLast();
System.out.println("移除最后一个: " + lastTask); // 清理日志

如果使用PriorityQueue,它本身不保证顺序,但SortedSet(例如TreeSet)可以实现SequencedSet,从而在保持排序的同时也能快捷访问首尾元素。

兼容性与使用建议

Sequenced Collections 是 Java 21 中的最终特性,没有预览阶段,可以直接用于生产。所有主要的集合类(包含在java.base模块中)都已经实现了新接口。不过,如果你需要向下兼容一些第三方库提供的自实现集合,可以通过instanceof检查接口是否存在:

if (collection instanceof SequencedCollection seq) {
    // 使用新方法
} else {
    // 降级处理
}

由于新接口默认提供了方法实现(尤其是addLastgetFirst等),对于自定义集合来说,实现这些接口也只需要很少的工作量。

总结

Sequenced Collections 虽然不像虚拟线程、模式匹配那样引人瞩目,但它实实在在填补了Java集合框架中“有序操作”这一长期缺失的抽象层。它没有引入新概念,只是把散落在不同实现类中的偶发方法收拢成了统一契约,让代码更符合直觉、更少出错。

如果你的项目已经升级到JDK 21,强烈建议你在日常编码中优先使用这些新方法——它们不仅减少样板代码,还能让很多算法(比如队列、栈、缓存)的意图更加明朗。对于正在学习Java的新人来说,了解这套接口也意味着能更快地上手标准库,而不必在不同集合类型的方法表中反复对照。

告别随机访问尴尬:Java 21 Sequenced Collections让有序集合操作开箱即用
收藏 (0) 打赏

感谢您的支持,我会继续努力的!

打开微信/支付宝扫一扫,即可进行扫码打赏哦,分享从这里开始,精彩与您同在
点赞 (0)

版权声明:
本站资源有的来自互联网收集整理,本站纯免费分享提供学习使用,如果侵犯了您的合法权益,请联系本站我们会及时删除。
本站资源仅供研究、学习交流之用,免费开源项目不代表完全可商用,若商业用途请先咨询开发企业能否商用,否则产生的一切后果将由下载用户自行承担。
原创板块未经允许不得转载,否则将追究法律责任。

淘吗网 java 告别随机访问尴尬:Java 21 Sequenced Collections让有序集合操作开箱即用 https://www.taomawang.com/server/java/2161.html

下一篇:

已经没有下一篇了!

常见问题

相关文章

猜你喜欢
发表评论
暂无评论
官方客服团队

为您解决烦忧 - 24小时在线 专业服务