有序集合(Sequenced Collections)#
版本信息
- 最低 JDK:21(随 JDK 21 引入,无预览)
- JEP:JEP 431: Sequenced Collections
为什么需要#
JDK 8 里「取集合的首/尾元素」竟然没有统一写法,每个类型各搞一套:
List:list.get(0)、list.get(list.size() - 1)Deque/ArrayDeque:peekFirst()、peekLast()SortedSet/NavigableSet:first()、last()LinkedHashSet:只能iterator().next()拿首元素,没有直接 APILinkedHashMap:要遍历到末尾才能拿最后一个 entry
反向遍历更别扭:Collections.reverse(list) 改原集合、或 listIterator 倒着走。JEP 431 给「有相遇顺序(encounter order)」的集合补了三个统一接口 SequencedCollection / SequencedSet / SequencedMap,并提供 getFirst / getLast / addFirst / addLast / removeFirst / removeLast / reversed 一套统一 API。
语法#
examples/sequenced-collections/ 里真实可运行的源码(snippets 嵌入,与示例零漂移):
package com.javamodern.sequenced;
import java.util.ArrayList;
import java.util.LinkedHashMap;
import java.util.LinkedHashSet;
import java.util.List;
// JEP 431(JDK 21):SequencedCollection / SequencedSet / SequencedMap
// —— 给「有相遇顺序(encounter order)」的集合统一首/尾访问与反向视图。
// List、LinkedHashSet、LinkedHashMap 现分别实现这三个接口,故直接拥有 getFirst/getLast/reversed 等。
public class SequencedCollectionsDemo {
public static Integer firstOfList(List<Integer> list) {
return list.getFirst(); // (1)
}
public static Integer lastOfList(List<Integer> list) {
return list.getLast();
}
/** reversed() 返回反向「视图」(非拷贝);这里拷一份便于展示与断言。 */
public static List<Integer> reversedList(List<Integer> list) {
return new ArrayList<>(list.reversed());
}
/** LinkedHashSet extends SequencedSet:按插入序取首元素。 */
public static String firstOfSet(LinkedHashSet<String> set) {
return set.getFirst();
}
public static void main(String[] args) {
List<Integer> nums = List.of(1, 2, 3);
System.out.println("first = " + firstOfList(nums));
System.out.println("last = " + lastOfList(nums));
System.out.println("reversed = " + reversedList(nums));
LinkedHashSet<String> ordered = new LinkedHashSet<>(List.of("a", "b", "c"));
System.out.println("set first = " + firstOfSet(ordered));
// SequencedMap:firstEntry / lastEntry / pollFirstEntry ...(LinkedHashMap 即实现)
LinkedHashMap<String, Integer> map = new LinkedHashMap<>();
map.put("Java", 8);
map.put("Kotlin", 1);
System.out.println("map first = " + map.firstEntry());
System.out.println("map last = " + map.lastEntry());
}
}
- :material-lightbulb:
List现在直接extends SequencedCollection,所以list.getFirst()/getLast()/reversed()开箱即用;LinkedHashSet与LinkedHashMap也各升级为SequencedSet/SequencedMap。
运行 SequencedCollectionsDemo.main 的实测输出(JDK 21):
first = 1
last = 3
reversed = [3, 2, 1]
set first = a
map first = Java=8
map last = Kotlin=1
与 JDK 8 旧写法对比#
Integer first = list.get(0);
Integer last = list.get(list.size() - 1);
// LinkedHashSet 取首:只能迭代
String first = set.iterator().next();
// 反向遍历:要么改原集合,要么倒序迭代器
Collections.reverse(list);
Integer first = list.getFirst();
Integer last = list.getLast();
String first = set.getFirst(); // LinkedHashSet 直接 getFirst
for (var e : list.reversed()) { ... } // reversed() 反向视图
var lastEntry = map.lastEntry(); // SequencedMap
底层原理#
JEP 431 在既有集合体系里插入了三个新接口(而非新建类型):SequencedCollection 继承 Collection,List/Deque/LinkedHashSet 等通过改造继承关系获得这些方法(LinkedHashSet → SequencedSet → SequencedCollection,LinkedHashMap → SequencedMap)。reversed() 返回的是一个反向视图(view)——对原集合的修改会反映到视图,反之亦然,零拷贝。
graph TD
SC["SequencedCollection<br/>getFirst/getLast/reversed"] --> L["List"]
SC --> D["Deque"]
SS["SequencedSet"] --> LHS["LinkedHashSet"]
SM["SequencedMap<br/>firstEntry/lastEntry/pollLastEntry"] --> LHM["LinkedHashMap"]
R["reversed() 返回反向视图"] -.零拷贝、修改互通.- SC
style SC fill:#bbdefb
style SM fill:#bbdefb
style R fill:#c8e6c9
常见坑 / 最佳实践#
reversed()是视图不是拷贝:list.reversed()改的是原集合的视角;要独立副本自己new ArrayList<>(list.reversed())。- 不可变集合的
add/remove抛异常:List.of(1,2,3).addFirst(0)抛UnsupportedOperationException(只读视图);getFirst/getLast/reversed这些只读操作正常。 SequencedMap的pollFirstEntry/pollLastEntry会变:它们移除并返回,对应可变 Map;不可变 Map 同样抛 UOE。- 「相遇顺序」≠「排序顺序」:
TreeSet不实现SequencedSet(它按比较序,相遇顺序语义模糊);LinkedHashSet才有稳定的插入序。
小结#
Sequenced Collections 把散落在各集合类型里的「首/尾/反转」写法收敛成一套统一 API,是 JDK 21 标准库里低调但高频的改进——取最后一个元素、反向遍历这类日常操作终于不必再查文档、因类型而异。