SequencedCollection 的 getFirst() 和 getLast() 用于安全获取首尾元素,空集合抛 NoSuchElementException;ArrayList 等未实现该接口故不支持,需用 LinkedHashSet 等兼容类型或判空防护。

SequencedCollection 的 getFirst() 和 getLast() 怎么用
JDK 21 引入 SequencedCollection 后,首尾访问不再需要手写 list.get(0) 或 list.get(list.size() - 1),也不用担心空集合抛 IndexOutOfBoundsException——但前提是用对了方法。这两个方法是默认实现,所有实现了该接口的集合(如 ArrayDeque、LinkedHashSet、LinkedHashMap::keySet() 等)都支持,但 ArrayList 和 HashSet 不行(它们没实现该接口)。
实操建议:
-
getFirst()和getLast()在空集合上调用会直接抛NoSuchElementException,不是NullPointerException,也不是静默返回null;务必先判空或用try-catch - 若你手头是
ArrayList,不能直接调用——得先转成支持的类型,比如new ArrayDeque(list),但注意这会产生额外对象开销 -
LinkedHashSet是常用且零拷贝的选择:它天然有序、去重、支持getFirst()/getLast(),适合做带顺序约束的缓存头尾管理
为什么 ArrayList 不能直接用 getFirst()
因为 ArrayList 没有实现 SequencedCollection 接口。JDK 设计者明确将“可高效首尾访问”和“支持顺序遍历”做了分离:SequencedCollection 要求实现类能以 O(1) 或摊还 O(1) 时间提供首尾元素,而 ArrayList 的 get(0) 虽快,但 get(size-1) 也快,它缺的是“语义承诺”——接口不实现,编译就过不去。
常见错误现象:
- 写
list.getFirst()编译报错:「cannot resolve method getFirst()」——说明变量类型是List或ArrayList,不是SequencedCollection - 强制转型
(SequencedCollection) list运行时报ClassCastException——ArrayList真的没实现这个接口 - 误以为
Stream.toList()返回的是SequencedCollection:实际返回的是不可变List(JDK 21 中是ImmutableCollections.ListN),也不支持
安全获取首尾的推荐写法(避免异常)
别依赖 try-catch 捕获 NoSuchElementException 来做流程控制——它本质是“异常路径”,性能差且掩盖逻辑意图。更清晰的做法是先查 isEmpty(),再取值。
示例(Java 21+):
SequencedCollection<String> seq = new LinkedHashSet<>(List.of("a", "b", "c"));
if (!seq.isEmpty()) {
String first = seq.getFirst();
String last = seq.getLast();
System.out.println(first + ", " + last); // a, c
}如果必须从普通 List 出发,且不想改数据结构,可用工具方法封装:
- 写一个静态方法
safeGetFirst(List<T> list),内部判空后返回list.isEmpty() ? null : list.get(0)(注意泛型擦除下null安全性) - 用
Optional包装:list.isEmpty() ? Optional.empty() : Optional.of(list.get(0)),语义更明确 - 切忌在循环里反复调用
getFirst()——某些实现(如基于链表的)虽是 O(1),但 JIT 可能无法优化掉重复调用开销
removeFirst() 和 removeLast() 的副作用要注意
这两个方法不仅返回元素,还会修改原集合。和 pollFirst() 类似,但命名更直白。关键点在于:它们是“破坏性操作”,且不同实现行为一致,但线程不安全。
使用场景与风险:
- 适合做 LRU 缓存淘汰:用
LinkedHashSet记录访问序,removeLast()淘最久未用项 - 多线程环境下必须加锁或换用
ConcurrentLinkedDeque(但它实现的是Deque,不是SequencedCollection,所以没有getFirst(),只有peekFirst()) -
removeFirst()对空集合同样抛NoSuchElementException,不是返回null,这点和pollFirst()不同——别混淆语义
真正容易被忽略的是:一旦用了这些方法,你就隐式承诺了集合的“可变序”语义。如果后续把集合传给只读逻辑(比如作为参数传进某个验证方法),而它内部又调用了 getFirst(),结果发现元素已被移除,问题会追溯困难。

















