【Java 21 新特性】顺序集合(Sequenced Collections)

1 摘要

引入新的接口表示具有定义的遇到顺序的集合。每个这样的集合都有一个明确定义的第一个元素、第二个元素,依此类推,直到最后一个元素。提供统一的API来访问它的第一个和最后一个元素,并以相反的顺序处理它的元素。

"生活只能向后理解;但必须向前生活。"—基尔克高德

2 动机

Java集合框架缺乏表示具有定义的遇到顺序的元素序列的集合类型。它还缺乏适用于这些集合的统一操作集。这些差距一直是问题和抱怨的重要来源。

如List和Deque都定义了遇到顺序,但共同父类Collection却没有定义遇到顺序。同样,Set没有定义遇到顺序,而子类型HashSet也没定义,但子类型如SortedSet和LinkedHashSet却定义了。因此,对遇到顺序的支持在类型层次结构中分散,使得在API中表达某些有用概念很困难,即不能在Collection中描述具有遇到顺序的参数或返回值。Collection太一般了,将这些约束规定到散文规范中,可能导致难以调试的错误。List太具体了,排除了SortedSet和LinkedHashSet。

FAQ

视图集合通常被迫降级到较弱语义。用Collections::unmodifiableSet包装LinkedHashSet会产生一个Set,丢弃了顺序信息。

没有定义它们的接口,与遇到顺序相关的操作要么不一致,要么缺失。虽许多实现支持获取第一个或最后一个元素,但每个集合都定义了自己的方式,有些不明显或完全缺失:

First elementLast element
Listlist.get(0)list.get(list.size() - 1)
Dequedeque.getFirst()deque.getLast()
SortedSetsortedSet.first()sortedSet.last()
LinkedHashSetlinkedHashSet.iterator().next()// missing
  • 一些是不必要的繁琐,如获取List的最后一个元素
  • 有些甚至没有英雄主义是不可能的:获取LinkedHashSet的最后一个元素的唯一方法是迭代整个集合!
  • 同样,从第一个元素到最后一个元素遍历通常需用迭代器或使用普通for循环,使代码冗长不直观

为解决这些问题,引入新接口SequencedCollection表示具有定义的遇到顺序的集合。每个SequencedCollection都有一个明确定义的第一个元素、第二个元素,依此类推,直到最后一个元素。它还提供统一的API访问它的第一个和最后一个元素,并以相反的顺序处理它的元素。

SequencedCollection还提供新reversed()方法,提供一个反向排序的视图。这视图可让集合以相反顺序处理元素,使用所有常见迭代机制,如增强for循环、显式的iterator()循环、forEach()、stream()、parallelStream()和toArray()。

如以前从LinkedHashSet获取反向排序的流困难,现只需linkedHashSet.reversed().stream()。

SequencedCollection还从Deque中提升一些方法,支持在两端添加、获取和删除元素:

  • void addFirst(E)
  • void addLast(E)
  • E getFirst()
  • E getLast()
  • E removeFirst()
  • E removeLast()

add(E)和remove()方法可选,主要是为支持不可修改的集合的情况。如集合为空,get()和remove()方法将抛出NoSuchElementException。

由于SequencedCollection的子接口具有冲突的定义,所以在SequencedCollection中没有定义equals()和hashCode()方法。

这些改动使得具有遇到顺序的集合更加易于使用和操作,并提供了一致的API来处理这些集合的元素。

Sequenced Sets

Set接口的扩展,有序集合,不含重复元素:

// since 21
interface SequencedSet<E> extends Set<E>, SequencedCollection<E> {
    SequencedSet<E> reversed();    // 协变重写
}

SequencedSet接口提供reversed()方法,用于返回一个反转顺序的SequencedSet。对于LinkedHashSet等集合,若元素已存在于集合中,则会将其移动到适当位置。这解决LinkedHashSet无法重新定位元素痛点。

SequencedMap

Map接口的扩展,条目具有定义好的遍历顺序。

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();
}
  • reversed()返回一个反转顺序的SequencedMap
  • sequencedKeySet()返回一个有序的键集合
  • sequencedValues()返回一个有序的值集合
  • sequencedEntrySet()返回一个有序的条目集合

SequencedMap还提供了putFirst()和putLast()方法,用于在指定位置插入键值对。对于SortedMap等映射,这些方法会抛出UnsupportedOperationException。

SequencedMap还提升了一些方法从NavigableMap接口,这些方法支持在两端获取和删除条目。

Retrofitting

为了将新的接口与现有的类和接口结构整合起来,调整:

img

  • List接口现在直接继承自SequencedCollection接口
  • Deque接口现在直接继承自SequencedCollection接口
  • LinkedHashSet类实现了SequencedSet接口
  • SortedSet接口现在直接继承自SequencedSet接口
  • LinkedHashMap类实现了SequencedMap接口
  • SortedMap接口现在直接继承自SequencedMap接口。

在适当的位置为reversed()方法提供了协变重写。如List#reversed()重写为返回一个List类型的值,而不是SequencedCollection类型的值。

还在Collections工具类中添加了一些新的方法,用于创建不可修改的包装器:

  • Collections.unmodifiableSequencedCollection(sequencedCollection)
  • Collections.unmodifiableSequencedSet(sequencedSet)
  • Collections.unmodifiableSequencedMap(sequencedMap)

替代方案

类型

将List接口重新定义为通用的有序集合类型。虽然List是有序的,但它也支持通过整数索引访问元素。许多有序的数据结构并不自然地支持索引,因此它们将被要求通过迭代来支持索引访问,这将导致索引访问的性能从O(1)变为O(n),延续了LinkedList的错误。

Deque作为通用的序列类型似乎是一个不错的选择,因为它已经支持了正确的一组操作。然而,它还包含了其他操作,包括一系列返回null的操作(offer、peek和poll),以及从Queue继承的操作。这些操作对于队列来说是合理的,但对于其他集合来说则不太合适。如果将Deque重新定义为通用的序列类型,那么List也将成为一个Queue,并支持栈操作,导致API变得混乱和令人困惑。

命名

术语"sequence"(序列)在这里被选择,它暗示了元素按照一定的顺序排列。它在各个平台上都被广泛使用,表示具有类似语义的集合。

术语"ordered"(有序)并不够具体。我们需要迭代两个方向上的元素,并在两端进行操作。一个有序的集合,如Queue,是一个明显的例外:它是有序的,但它也明显是不对称的。

术语"reversible"(可逆)在之前的版本中使用过,但它并没有立即唤起双端的概念。也许更大的问题是,Map变体将被命名为ReversibleMap,这会误导地暗示它支持通过键和值进行查找(有时称为BiMap或BidiMap)。

Add, put, and UnsupportedOperationException

对于通过相对比较确定顺序的集合,例如SortedSet的addFirst和SortedMap的putLast方法会抛出UnsupportedOperationException。一些集合不实现所有SequencedCollection操作的不对称性可能看起来不太好。然而,这是有价值的,因为它将SortedSet和SortedMap纳入到有序集合的家族中,使它们可以比以前更广泛地使用。这种不对称性也与集合框架中的先前设计决策保持一致。例如,Map的keySet方法返回一个Set,即使实际返回的实现不支持添加操作。

另一种方法是通过重新调整接口来保持添加操作的独立性。这将导致在结构上有很薄的语义的新接口类型(例如AddableCollection),在实践中没有用处,并且会使类型层次结构变得混乱。

测试

我们将在JDK的回归测试套件中添加一套全面的测试。

风险和假设

在继承层次结构中高层次地引入新的方法可能会导致对明显方法名称(如reversed()和getFirst())的冲突。

特别需要关注的是List和Deque上reversed()方法的协变重写。这些重写与已经实现了List和Deque的现有集合在源代码和二进制兼容性上是不兼容的。在JDK中有两个这样的集合的例子:LinkedList和一个内部类sun.awt.util.IdentityLinkedList。LinkedList类通过在LinkedList本身上引入一个新的reversed()协变重写来处理。内部的IdentityLinkedList类被删除,因为它不再需要。

提案的早期版本在SequencedMap接口上引入了keySet()、values()和entrySet()方法的协变重写。经过一些分析,确定这种方法引入了不兼容性的风险太大;实际上,它使任何现有的子类都无效。选择了另一种方法,即在SequencedMap中引入了新的sequencedKeySet()、sequencedValues()和sequencedEntrySet()方法,而不是调整现有方法为协变重写。回顾起来,这可能是因为在Java 6中引入navigableKeySet()方法时采用了类似的方法,而不是修改现有的keySet()方法为协变重写。

有关不兼容性风险的完整分析,请参见附加到CSR(JDK-8266572)的报告。

历史

这个提案是我们2021年ReversibleCollections提案的一个增量演进。与该提案相比,主要的变化是改名、引入SequencedMap接口以及引入不可修改的包装器方法。

ReversibleCollection提案又基于Tagir Valeev的2020年OrderedMap/OrderedSet提案。该提案中的一些基本概念仍然存在,尽管在细节上有很多不同。

多年来,我们收到了许多关于将List与Set或Map结合的请求和提案。这些请求包括4152834、4245809、4264420、4268146、6447049和8037382。

其中一些请求在Java 1.4中引入的LinkedHashSet和LinkedHashMap中部分得到了满足。虽然这些类满足了一些用例,但它们的引入留下了集合框架中抽象和操作的空白,如上所述。

测试

我们将向JDK的回归测试套件中添加一套全面的测试。

风险和假设

在继承层次结构中引入新的方法存在冲突的风险,例如reversed()和getFirst()这样的明显方法名称。 特别关注的是List和Deque上的covariant overrides的reversed()方法。这些方法与已实现List和Deque的现有集合在源代码和二进制上不兼容。在JDK中有两个这样的集合的示例:LinkedList和一个内部类sun.awt.util.IdentityLinkedList。LinkedList类通过在LinkedList本身上引入了一个新的reversed() covariant override来处理。内部的IdentityLinkedList类被删除,因为它不再需要。 提案的早期版本在SequencedMap接口的keySet()、values()和entrySet()方法上引入了covariant overrides。经过一些分析,确定这种方法引入了太大的不兼容风险;实质上,它使任何现有的子类无效。选择了另一种替代方法,即在SequencedMap中引入新的方法sequencedKeySet()、sequencedValues()和sequencedEntrySet(),而不是调整现有方法为covariant overrides。回顾起来,可能出于同样的原因,在Java 6中引入navigableKeySet()方法时采取了类似的方法,而不是修改现有的keySet()方法为covariant override。 有关不兼容风险的完整分析,请参阅附加到CSR(JDK-8266572)的报告。

参考

  • 编程严选网专栏-JDK21新特性

    本文由博客一文多发平台 OpenWrite 发布!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/286647.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

【算法与数据结构】968、LeetCode监控二叉树

文章目录 一、题目二、解法三、完整代码 所有的LeetCode题解索引&#xff0c;可以看这篇文章——【算法和数据结构】LeetCode题解。 一、题目 二、解法 思路分析&#xff1a;本题的一共有两个难点&#xff0c;一个在于如何遍历二叉树&#xff08;前中后遍历&#xff0c;选择什么…

MySQL 时间日期函数,流程控制函数,加密解密函数以及聚合查询函数

注:本文仅作为查找函数和部分理解使用,希望能给大家带来帮助 以下函数均可以使用 SELECT NOW()等函数 FROM DUAL;来测试 //其中dual是一个准们用来测试的测试表 1.时间日期函数 1.1 获取时间的函数 重点记忆前三个红色标注的函数, 第一个函数返回值如2024-01-02的形式 第二个如…

如何使用curl在PHP中同时上传文件和其他数据?

问CHAT&#xff1a;举个例子说明如何使用curl在PHP中同时上传文件和其他数据&#xff1f; CHAT回复&#xff1a;以下例子为&#xff1a; php <?php $url http://www.example.com/path/; $filename path/to/your/file.png; $fields array( fieldParam1 > someValue, …

C++摸版(初阶)----函数模版与类模版

本专栏内容为&#xff1a;C学习专栏&#xff0c;分为初阶和进阶两部分。 通过本专栏的深入学习&#xff0c;你可以了解并掌握C。 &#x1f493;博主csdn个人主页&#xff1a;小小unicorn ⏩专栏分类&#xff1a;C &#x1f69a;代码仓库&#xff1a;小小unicorn的代码仓库&…

Mac环境下反编译apk

Mac环境下反编译apk 安装反编译工具dex2jar&#xff1a;[官网下载](https://sourceforge.net/projects/dex2jar/)JD-GUI&#xff1a;[官网下载](https://jd-gui.apponic.com/) 实操1. 将需要反编译的 .apk 文件放在下载的 dex2jar 文件夹目录下2. 使用 cd /xxx/dex2jar-2.0 命令…

深度生成模型之GAN的评估 ->(个人学习记录笔记)

文章目录 深度生成模型之GAN的评估图像翻译的应用1. 风格迁移2. 数据增强3. 经典图像任务4. 内容创作5. 人脸图像编辑6. 人体图像编辑 图像翻译模型1. 有监督图像翻译模型2. 无监督图像翻译模型3. 多域图像翻译模型 深度生成模型之GAN的评估 图像翻译的应用 1. 风格迁移 各类…

120基于matlab的LMS自适应滤波算法

基于matlab的LMS自适应滤波算法&#xff0c;如、解相关LMS算法&#xff0c;滤波型LMS算法&#xff0c;变换域LMS算法&#xff0c;输出滤波前后及学习曲线图。数据可更换自己的&#xff0c;程序已调通&#xff0c;可直接运行。 120自适应滤波算法变换域LMS算法 (xiaohongshu.com…

k8s的陈述式管理

k8s的陈述式管理&#xff1a; 所谓的陈述式管离也就是命令行工具 优点&#xff1a;90%以上都可以满足 对资源的增删查比较方便&#xff0c;对改不是很友好 缺点&#xff1a;命令比较冗长&#xff0c;复杂&#xff0c;难记 声明式&#xff1a; k8s当中的YAML文件来实现资源管…

测试要不要写测试用例(多方面剖析)

前言 最近在网上看到关于这样的一个话题“测试需要写测试用例吗&#xff1f;”&#xff0c;引起了很多同学的讨论。这段话是这样的&#xff1a; 测试用例主要作用&#xff1a;有效地评估软件的质量&#xff0c;测试用例质量体现了测试的质量。 下面摘取一部分同学的观点&…

对比开源大语言模型的自然语言生成SQL能力

背景 NL-to-SQL&#xff08;自然语言到结构化查询语言&#xff09;任务是自然语言处理&#xff08;NLP&#xff09;领域的一个难题。 它涉及将自然语言问题转换为 SQL 查询&#xff0c;然后可以针对关系数据库执行该查询来回答问题。 该任务是 NLP 中的一个专门子领域&#xf…

Java企业电子招投标系统源代码,支持二次开发,采用Spring cloud框架

在数字化采购领域&#xff0c;企业需要一个高效、透明和规范的管理系统。通过采用Spring Cloud、Spring Boot2、Mybatis等先进技术&#xff0c;我们打造了全过程数字化采购管理平台。该平台具备内外协同的能力&#xff0c;通过待办消息、招标公告、中标公告和信息发布等功能模块…

SQL 在已有表中修改列名的方法

文章目录 1. MySQL2. SQL Server3. Oracle / PostgreSQL Question&#xff1a; 假设有一张表 StudentInfo&#xff0c;表中有一个列名是 Student_Name &#xff0c;想要把这个列名改成 StudentName 应该如何操作&#xff1f; 建表语句如下&#xff1a; --建表 if object_id(S…

Java-网络爬虫(一)

文章目录 前言一、网络爬虫1. 介绍2. 爬虫协议3. 法律法规 二、相关知识1. HttpClient2. Jsoup 三、综合案例1. 案例一2. 案例二 四、总结 前言 在大数据时代&#xff0c;信息采集是一项重要的工作&#xff0c;而互联网中的数据是海量的&#xff0c;如果单纯靠人力进行信息获取…

MATLAB习题操作实战

2.1创建一个有7个元素的一维数组&#xff0c;并做如下处理:直接寻访一维数组的第6个元素;寻访一维数组的第1、3、5个元素;寻访一维数组中第4个至最后1个元素;寻访一维数组中大于70的元素。 % 创建一维数组 array [50, 60, 70, 80, 90, 100, 110];% 直接寻访一维数组的第6个元…

【自动驾驶中的SLAM技术】第2讲:基础数学知识回顾

第二讲&#xff1a;基础数学回顾 文章目录 第二讲&#xff1a;基础数学回顾1 几何学1.1 坐标系1.2 坐标变换① 空间向量② 基变换③ 坐标变换④ 总结 1.3 四元数与旋转向量 2 运动学2.1 李群视角2.2 四元数视角2.3 四元数的李代数与旋转向量间的转换2.4 SO(3)t 上的运动学2.5 线…

六、HTML 段落

HTML 可以将文档分割为若干段落。 一、HTML 段落 段落是通过 <p> 标签定义的。 <p>这是一个段落 </p> <p>这是另一个段落</p> 注意&#xff1a;浏览器会自动地在段落的前后添加空行。&#xff08;</p> 是块级元素&#xff09; 二、不…

算法巡练day03Leetcode203移除链表元素707设计链表206反转链表

今日学习的文章视频链接 https://www.bilibili.com/video/BV1nB4y1i7eL/?vd_source8272bd48fee17396a4a1746c256ab0ae https://programmercarl.com/0707.%E8%AE%BE%E8%AE%A1%E9%93%BE%E8%A1%A8.html#%E7%AE%97%E6%B3%95%E5%85%AC%E5%BC%80%E8%AF%BE 链表理论基础 见我的博…

Linux 命令echo

命令作用 输出一行字符串在shell中&#xff0c;可以打印变量的值输出结果写入到文件在显示器上显示一段文字&#xff0c;起到提示的作用 语法 echo [选项] [字符串] 参数 字符含义-n不自动换行-e解释转义字符-E不解释转义字符 如果-e有效&#xff0c;则识别以下序列&…

2024,这将是量子计算的真正挑战

2023年&#xff0c;一项项量子计算纪录被打破。 谷歌量子AI团队证明了将多个量子比特分组合成为一个逻辑量子比特的纠错方法可以提供更低的容错率。以往的纠错研究随着比特数的增加&#xff0c;错误率会提高&#xff0c;都是“越纠越错”&#xff0c;而这次谷歌首次实现了“越纠…

K8S本地开发环境-minikube安装部署及实践

引言 在上一篇介绍了k8s的入门和实战&#xff0c;本章就来介绍一下在windows环境如何使用minikube搭建K8s集群&#xff0c;好了废话不多说&#xff0c;下面就和我一起了解Minikube吧。 什么是Minikube&#xff1f; Minikube 是一种轻量级的 Kubernetes 实现&#xff0c;可在本…