跳转至

有序集合(Sequenced Collections)#

版本信息

为什么需要#

JDK 8 里「取集合的首/尾元素」竟然没有统一写法,每个类型各搞一套:

  • Listlist.get(0)list.get(list.size() - 1)
  • Deque/ArrayDequepeekFirst()peekLast()
  • SortedSet/NavigableSetfirst()last()
  • LinkedHashSet:只能 iterator().next() 拿首元素,没有直接 API
  • LinkedHashMap:要遍历到末尾才能拿最后一个 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());
    }
}
  1. :material-lightbulb: List 现在直接 extends SequencedCollection,所以 list.getFirst() / getLast() / reversed() 开箱即用;LinkedHashSetLinkedHashMap 也各升级为 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 继承 CollectionList/Deque/LinkedHashSet 等通过改造继承关系获得这些方法(LinkedHashSet → SequencedSet → SequencedCollectionLinkedHashMap → 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 这些只读操作正常。
  • SequencedMappollFirstEntry/pollLastEntry 会变:它们移除并返回,对应可变 Map;不可变 Map 同样抛 UOE。
  • 「相遇顺序」≠「排序顺序」TreeSet 不实现 SequencedSet(它按比较序,相遇顺序语义模糊);LinkedHashSet 才有稳定的插入序。

小结#

Sequenced Collections 把散落在各集合类型里的「首/尾/反转」写法收敛成一套统一 API,是 JDK 21 标准库里低调但高频的改进——取最后一个元素、反向遍历这类日常操作终于不必再查文档、因类型而异。

参考#