1. 概述

Java 21 预计于2023年9月发布,作为继Java 17之后的下一个长期支持版本。在新特性中,Java的集合框架迎来了一项重要更新——**有序集合 (Sequenced Collections)**。

这项新特性堪称颠覆性改进,它将彻底改变开发者与集合的交互方式。通过向现有继承体系中注入新接口,提供了直接访问集合首尾元素的内置方法,同时支持获取集合的反向视图。

本文将深入探讨这一新特性、潜在风险及其带来的优势。

2. 动机

缺乏具有明确定义顺序的通用集合超类型,一直是Java开发中的痛点。特别是缺少统一的首尾元素访问方法和反向迭代支持,已成为Java集合框架的长期短板。

ListDeque为例:两者都定义了元素顺序,但它们的共同超类型Collection却没有。同样,Set本身不定义顺序,但某些子类型如SortedSetLinkedHashSet却具备顺序特性。这种顺序支持分散在类型体系中,导致相关操作要么不一致,要么直接缺失。

为说明这种不一致性,我们对比不同集合类型的首尾元素访问方式:

集合类型 获取首元素 获取尾元素
List list.get(0) list.get(list.size() - 1)
Deque deque.getFirst() deque.getLast()
SortedSet sortedSet.first() sortedSet.last()
LinkedHashSet linkedHashSet.iterator().next() // 缺失方法

获取集合反向视图时同样存在混乱。虽然从首到尾的迭代模式清晰一致,但反向操作却充满挑战:

  • NavigableSet 使用 descendingSet()
  • Deque 使用 descendingIterator()
  • List 使用 listIterator()
  • LinkedHashSet 则完全不支持反向迭代

这些差异导致代码库碎片化,增加了复杂度,使得在API中表达某些通用概念变得异常困难。

3. 新的Java集合继承体系

该特性引入了三个新接口:有序集合(Sequenced Collection)、有序集(Sequenced Set)和有序映射(Sequenced Map),它们被整合到现有集合继承体系中:

新集合继承体系

图片来源:JEP 431: Sequenced Collections 官方文档

3.1. SequencedCollection

有序集合是具有明确定义元素顺序的Collection 新的SequencedCollection接口提供了在集合两端添加、获取或删除元素的方法,以及获取集合反向视图的方法:

interface SequencedCollection<E> extends Collection<E> {

    // 新增方法
    SequencedCollection<E> reversed();

    // 从Deque提升的方法
    void addFirst(E);
    void addLast(E);

    E getFirst();
    E getLast();

    E removeFirst();
    E removeLast();
}

✅ 除reversed()外,所有方法都是默认方法,实现从Deque提升而来
reversed()提供原始集合的反向视图,对原集合的修改会同步反映在反向视图中
⚠️ add*()remove*()方法在不可修改集合或已定义排序的集合中会抛出UnsupportedOperationException
⚠️ 当集合为空时,get*()remove*()方法会抛出NoSuchElementException

3.2. SequencedSet

有序集是特殊的Set,它同时作为SequencedCollection工作,并确保元素唯一性。 SequencedSet接口继承SequencedCollection并重写其reversed()方法,唯一区别是返回类型变为SequencedSet

interface SequencedSet<E> extends Set<E>, SequencedCollection<E> {

    // 协变重写
    SequencedSet<E> reversed();
}

3.3. SequencedMap

有序映射是具有明确定义条目顺序的Map SequencedMap不继承SequencedCollection,而是提供自己的方法来操作集合两端的元素:

interface SequencedMap<K, V> extends Map<K, V> {
    
    // 新增方法
    SequencedMap<K, V> reversed();
    SequencedSet<K> sequencedKeySet();
    SequencedCollection<V> sequencedValues();
    SequencedSet<Entry<K, V>> sequencedEntrySet();

    V putFirst(K, V);
    V putLast(K, V);

    // 从NavigableMap提升的方法
    Entry<K, V> firstEntry();
    Entry<K, V> lastEntry();
    Entry<K, V> pollFirstEntry();
    Entry<K, V> pollLastEntry();
}

put*()方法在不可修改映射或已定义排序的映射中会抛出UnsupportedOperationException
⚠️ 在空映射上调用从NavigableMap提升的方法会抛出NoSuchElementException

4. 潜在风险

新接口的引入本不应影响仅使用集合实现的代码,但如果代码库中定义了自定义集合类型,可能会出现以下冲突:

  • 方法命名冲突:新增方法可能与现有类中的方法冲突。例如,如果自定义的List实现已定义getFirst()方法但返回类型与SequencedCollection中的不同,升级到Java 21时会导致源码不兼容
  • 协变重写冲突ListDeque都对reversed()方法进行了协变重写(分别返回ListDeque)。任何同时实现这两个接口的自定义集合在升级时会导致编译错误,因为编译器无法确定选择哪个重写版本

完整风险分析可参考JDK-8266572报告。

5. 总结

有序集合 (Sequenced Collections) 标志着Java集合框架的重大进步。通过解决长期存在的统一处理有序集合的需求,Java使开发者能够更高效、更直观地工作。新接口建立了更清晰的结构和一致的行为,从而产生更健壮、更易读的代码。

本文源码可在GitHub获取。


原始标题:Sequenced Collections in Java | Baeldung