Java 中的有序集合 (Sequenced Collections)

1. 概述

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

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

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

2. 动机

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

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

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

集合类型

获取首元素

获取尾元素

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 extends Collection {

// 新增方法

SequencedCollection 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 extends Set, SequencedCollection {

// 协变重写

SequencedSet reversed();

}

3.3. SequencedMap

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

interface SequencedMap extends Map {

// 新增方法

SequencedMap reversed();

SequencedSet sequencedKeySet();

SequencedCollection sequencedValues();

SequencedSet> sequencedEntrySet();

V putFirst(K, V);

V putLast(K, V);

// 从NavigableMap提升的方法

Entry firstEntry();

Entry lastEntry();

Entry pollFirstEntry();

Entry pollLastEntry();

}

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

4. 潜在风险

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

方法命名冲突:新增方法可能与现有类中的方法冲突。例如,如果自定义的List实现已定义getFirst()方法但返回类型与SequencedCollection中的不同,升级到Java 21时会导致源码不兼容

协变重写冲突:List和Deque都对reversed()方法进行了协变重写(分别返回List和Deque)。任何同时实现这两个接口的自定义集合在升级时会导致编译错误,因为编译器无法确定选择哪个重写版本

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

5. 总结

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

本文源码可在GitHub获取。