编程技术是改变世界的力量。
本站
当前位置:网站首页 > 后端语言 > 正文

java21 有序集合 java21下载

gowuye 2024-04-04 11:56 8 浏览 0 评论

JDK 21引入的三个接口代表了具有定义的顺序遍历顺序的集合。每个集合都有一个明确定义的第一个元素、第二个元素等,直到最后一个元素。它们提供了统一的API来访问它们的第一个和最后一个元素,并以正向和反向顺序处理它们的元素。

在JDK 21之前,Java集合框架缺乏表示具有定义的遍历顺序的元素序列的集合类型。例如,List和Deque定义了一个遍历顺序,但它们的公共超类型Collection却没有。同样,Set和子类型如HashSet不定义遍历顺序,而子类型如SortedSet和LinkedHashSet则定义了。鉴于缺乏具有定义的遍历顺序的集合类型,就没有统一的操作集合来遵守遍历顺序。虽然有一些操作遵守遍历顺序,但它们并不统一。

一个例子是在集合框架中缺少一个常见的有序相关操作,比如获取Deque和List的第一个元素。要获取Deque的第一个元素,您使用getFirst()方法。然而,要获取List的第一个元素,您使用get(0)。

遍历顺序的支持分散在类型层次结构中,使得很难在API中表达某些有用的概念。既然Collection既不具体也不抽象,那么要描述具有遍历顺序的参数或返回值是不可能的,并且可能导致难以调试的错误。如果API想要接收具有定义的遍历顺序的集合,那么使用List就太具体了,因为它排除了SortedSet和LinkedHashSet。相关问题是视图集合经常被迫降级为更弱的语义。例如,使用Collections::unmodifiableSet包装LinkedHashSet会产生一个丢弃遍历顺序信息的Set。

由于没有定义它们的接口,与遍历顺序相关的操作要么不一致,要么缺失。许多实现支持获取第一个或最后一个元素,但每个集合都定义了自己的方法,有些方法不明显或根本不存在。

通过Sequenced接口更新集合框架

从JDK 21开始,JEP 431引入了三个Java集合框架接口,用于创建序列化集合、序列化集合和序列化映射:

  • SequencedCollection
  • SequencedSet
  • SequencedMap 这三个接口为Java集合框架提供了一种表示具有定义的遍历顺序的元素序列的集合类型,并在集合上应用了一组统一的操作。这些接口适合于集合类型层次结构,如下图所示。

该图显示了将SequencedCollection、SequencedSet和SequencedMap接口集成到Java集合框架的类和接口层次结构中的以下调整:

  • 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)

有关顺序集合、顺序集和顺序映射接口的背景信息,请参阅JEP 431

SequencedCollection

一个SequencedCollection是在JDK 21中新增的一种集合类型,它表示具有定义的遭遇顺序的元素序列。

SequencedCollection具有首尾元素,它们之间的元素具有后继和前驱。 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()方法提供了原始集合的反向排序视图。对原始集合的任何修改都会反映在视图中。

返回视图中元素的遭遇顺序是该集合元素遭遇顺序的反向。反向排序会影响所有对顺序敏感的操作,包括对返回视图的视图集合的操作。

底层集合的更改可能会反映在反向视图中,也可能不会,这取决于实现。如果允许,对视图的修改会“写入”到原始集合。反向排序视图使所有不同的顺序类型都能够使用所有常见的迭代机制以两个方向处理元素:

  • 增强型for循环
  • 显式iterator()循环
  • forEach()
  • stream()
  • parallelStream()
  • toArray() 例如,从LinkedHashSet获取一个反向排序的流以前相当困难;现在只需:
linkedHashSet.reversed().stream()

注意:reversed()方法本质上是一个重命名的NavigableSet::descendingSet,提升为SequencedCollection。

SequencedCollection的以下方法是从Deque提升的。它们支持在两端添加、获取和删除元素:

  • void addFirst(E)
  • void addLast(E)
  • E getFirst()
  • E getLast()
  • E removeFirst()
  • E removeLast()
  • add*(E)和remove*()方法是可选的,主要是为了支持不可修改的集合情况。如果集合为空,则get*()和remove*()方法会抛出NoSuchElementException。在SequencedCollection中没有equals()和hashCode()的定义,因为它的子接口具有冲突的定义。

SequencedSet

一个SequencedSet既是一个SequencedCollection也是一个Set。

可以将SequencedSet视为一个同时具有良好定义的遭遇顺序的Set,或者将其视为一个同时具有唯一元素的SequencedCollection。

javaCopy code
interface SequencedSet<E> extends Set<E>, SequencedCollection<E> {
    SequencedSet<E> reversed();    // 协变覆盖
}

该接口对equals和hashCode方法的要求与Set.equals和Set.hashCode中定义的相同。如果一个Set和一个SequencedSet具有相等的元素,则它们比较相等,而与顺序无关。

SequencedSet定义了reversed()方法,它提供了这个集合的反向排序视图。与SequencedCollection.reversed方法唯一的区别是SequencedSet.reversed的返回类型是SequencedSet。

在SequencedSet中,SequencedCollection的add*(E)方法执行以下操作:

  • addFirst(E) - 将元素添加为集合的第一个元素。
  • addLast(E) - 将元素添加为集合的最后一个元素。 SequencedCollection的add*(E)方法还具有针对LinkedHashSet和SortedSet的以下特殊情况行为。

对于LinkedHashSet的特殊情况行为:

  • 对于诸如LinkedHashSet之类的集合,addFirst(E)和addLast(E)方法具有特殊的语义。如果条目已经存在于集合中,则LinkedHashSet将重新定位该条目。如果元素已经存在于集合中,则将其移动到适当的位置。这解决了LinkedHashSet的一个长期缺陷,即无法重新定位元素。 对于SortedSet的特殊情况行为:
  • 诸如SortedSet之类的集合通过相对比较来定位元素,无法支持诸如SequencedCollection超接口中声明的addFirst(E)和addLast(E)方法等显式定位操作。这些方法会抛出UnsupportedOperationException异常。

SequencedMap

一个SequencedMap提供了在映射的遭遇顺序两端添加映射、检索映射和删除映射的方法。该接口还定义了reversed()方法,它提供了这个映射的反向排序视图。

SequencedMap具有良好定义的遭遇顺序,支持两端操作,并且可逆。一个映射的反向排序视图通常不可序列化,即使原始映射是可序列化的。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();
}

sequencedKeySet()、sequencedValues()和sequencedEntrySet()方法与Map接口的keySet()、values()和entrySet()方法完全类似。所有这些方法都返回基础集合的视图;对视图的修改在基础集合中可见,反之亦然。这些视图的遭遇顺序与基础映射的遭遇顺序完全对应。

SequencedMap接口方法与Map方法的区别在于sequenced*()方法具有一个sequenced返回类型:

  • 在SequencedSet<K> sequencedKeySet()中,实现返回映射的keySet的SequencedSet视图,并表现如下:
  • add和addAll方法抛出UnsupportedOperationException异常。
  • reversed方法返回映射反向视图的sequencedKeySet视图。
  • 其其他方法调用映射的keySet视图的相应方法。
  • 在SequencedCollection<V> sequencedValues()中,实现返回映射值集合的SequencedCollection视图,并表现如下:
  • add和addAll方法抛出UnsupportedOperationException异常。
  • reversed方法返回映射反向视图的sequencedValues视图。
  • equals和hashCode方法从Object继承。
  • 其其他方法调用映射的值视图的相应方法。
  • 在SequencedSet<Entry<K,V>> sequencedEntrySet()中,实现返回映射entrySet的SequencedSet视图,并表现如下:
  • add和addAll方法抛出UnsupportedOperationException异常。
  • reversed方法返回映射反向视图的sequencedEntrySet视图。
  • 其其他方法调用映射的entrySet视图的相应方法。 put*(K, V)方法具有特殊情况语义,类似于SequencedSet的相应add*(E)方法:
  • 对于诸如LinkedHashMap之类的映射,如果条目已经存在于映射中,它们会重新定位条目。
  • 对于诸如SortedMap之类的映射,这些方法抛出UnsupportedOperationException异常。 从NavigableMap提升的以下方法支持在两端获取和删除条目:
  • firstEntry()
  • lastEntry()
  • pollFirstEntry()
  • pollLastEntry() firstEntry()、lastEntry()、pollFirstEntry()和pollLastEntry()方法返回Map.Entry实例,表示调用时的映射快照。它们不支持通过可选的setValue方法对基础映射进行变异。

ArrayList 和 LinkedHashMap 反转示例

演示集合的逆序视图

以下示例演示了序列化接口的reversed()方法如何生成集合的逆序视图,以及修改逆序视图如何影响原始集合,以及如何在原始集合中进行的修改在逆序视图中是可见的。

逆序视图是"实时"的,而不是集合的快照。通过使用ArrayList及其逆序视图,以下示例说明了这一特性。

注意:以下示例代码中不包括不必要的jshell输出。

开始一个jshell会话并使用ArrayList类创建一个字符串对象列表。

jshell> var list = new ArrayList<>(Arrays.asList("a", "b", "c", "d", "e"))
list ==> [a, b, c, d, e]

接下来,使用reversed()方法生成集合的逆序视图。

jshell> var rev = list.reversed()
rev ==> [e, d, c, b, a]

当修改逆序视图时,它会影响到原始集合。将f作为一个条目添加到逆序视图中,然后验证它是否被添加到原始集合中。

jshell> rev.add(1, "f")
jshell> rev
rev ==> [e, f, d, c, b, a]
jshell> list
list ==> [a, b, c, d, f, e]

当修改原始集合时,修改会在逆序视图中可见。将索引2处的元素设置为X,验证它是否添加到集合中,然后生成修改后集合的逆序视图。

jshell> list.set(2, "X")
jshell> list
list ==> [a, b, X, d, f, e]
jshell> rev
rev ==> [e, f, d, X, b, a]

演示LinkedHashMap视图的组合

除了使用ArrayList之外,逆序()视图还可以由其他视图组成,例如List.subList().reversed()或SequencedMap.sequencedKeySet().reversed()和SequencedMap.reversed().sequencedKeySet()。

SequencedMap.sequencedKeySet().reversed()和SequencedMap.reversed().sequencedKeySet()视图在功能上是等效的,并且通过使用LinkedHashMap类在下面的示例代码中进行说明。

首先,启动一个jshell会话,并使用LinkedHashMap类创建一个String对象的映射。

jshell> var map = new LinkedHashMap<String, Integer>()
jshell> map.put("a", 1)
jshell> map.put("b", 2)
jshell> map.put("c", 3)
jshell> map.put("d", 4)
jshell> map.put("e", 5)
map ==> {a=1, b=2, c=3, d=4, e=5}

接下来,使用reversed()方法生成原始集合的keySet视图的逆序视图。

jshell> map.sequencedKeySet().reversed()
$17 ==> [e, d, c, b, a]

演示SequencedMap不支持通过使用可选的setValue方法对基础map进行变异

这个演示说明了SequencedMap部分的最后一句话,即firstEntry()、lastEntry()、pollFirstEntry()和pollLastEntry()方法不支持通过使用可选的setValue方法对基础映射进行变异。

尝试使用setValue()改变基础映射中的一个条目将会抛出UnsupportedOperationException异常。这与通过迭代entrySet获得的映射条目的更改形成对比。如果调用map.entrySet().iterator().next()来返回一个映射条目,然后在条目上调用setValue(),它将修改原始映射。

打开一个jshell会话,并使用演示组合的LinkedHashMap视图中生成的映射。

调用map.entrySet().iterator().next()来返回第一个映射条目。

jshell> var entry = map.entrySet().iterator().next()
entry ==> a=1

使用setValue()将映射条目的值更改为77。这个条目是通过迭代entrySet获得的,所以它可以在原始映射中修改。验证映射中的值是否更改为77。

jshell> entry.setValue(77)
$19 ==> 1
jshell> map
map ==> {a=77, b=2, c=3, d=4, e=5}

尝试使用setValue()将映射条目更改为999。因为映射条目不是通过entrySet迭代获取的,所以它抛出了UnsupportedOperationException异常。

jshell> entry = map.firstEntry()
entry ==> a=77
jshell> entry.setValue(999)
 | Exception java.lang.UnsupportedOperationException: not supported
 | at NullableKeyValueHolder.setValue (NullableKeyValueHolder.java:126)
 | at (#22:1)

相关推荐

爱上开源之golang入门至实战第四章-切片(Slice)

前言Go数组的长度不可改变,在特定场景中这样的集合就不太适用,Go中提供了一种灵活,功能强悍的内置类型切片("动态数组"),与数组相比切片的长度是不固定的,可以追加元素,在追加时可...

Go语言入门必知教程-切片

切片是一种灵活的和可扩展的数据结构,用于实现和管理数据集。切片由多个元素组成,所有元素都是相同类型的。切片是动态数组的一部分,可以根据需要进行增长和收缩。与数组一样,切片也可以索引。切片具有容量和长度...

Go语言基础-切片

切片是什么?切片是Go语言的一种数据结构。和数组相似,不过切片可以在它的结尾增加更多的元素。这样可变长度在实际编程中更为有用。声明切片切片的声明和数组也很相似,只是声明切片时不需要指定大小。例:va...

5分钟掌握GO中切片的基本使用方法

最近Golang越来越火,不少小伙伴都纷纷开始学习Golang,但对于原先为C++或者JAVA的同学,用习惯了数据、list、vector等,会对Go的切片slice不习惯,下面整理出go中slice...

揭秘 Go 切片(Slice)的秘密

当向切片添加新参数时,底层数组会发生什么变化?它会扩展以容纳更多元素吗?在这篇文章中,我们将深入探讨切片的内部工作原理,以及如何利用这些知识来进行更好的内存管理和性能优化。具体而言,我们将探索Go...

【Go语言slice详解】深入掌握Go语言中的slice类型及常用操作!

Go语言中的slice(切片)是一种非常方便的数据结构,可以动态地增加或减少其元素数量,且可以访问底层数组的任意一个子序列。本文将对Go语言中的slice进行详细的讲解。Slice的定义在Go语言中,...

掌握GO中的Slice,这就够了

最近Golang越来越火,不少小伙伴都纷纷开始学习Golang,但对于原先为C++或者JAVA的同学,用习惯了数据、list、vector等,会对Go的切片slice不习惯,下面整理出go中slice...

golang2021面向对象(26)Go语言类型内嵌和结构体内嵌

结构体可以包含一个或多个匿名(或内嵌)字段,即这些字段没有显式的名字,只有字段的类型是必须的,此时类型也就是字段的名字。匿名字段本身可以是一个结构体类型,即结构体可以包含内嵌结构体。?可以粗略地将这个...

2022-11-13:以下go语言代码中,如何获取结构体列表以及结构体内

2022-11-13:以下go语言代码中,如何获取结构体列表以及结构体内的指针方法列表?以下代码应该返回{"S1":["M1","M2"],"S...

Go语言文件和目录操作

文件和目录操作概述一、文件和目录操作概述在计算机中,文件和目录是存储数据的重要方式。在Go语言中,我们可以使用os和io/ioutil包提供的函数和结构体来进行文件和目录操作。本文将详细介绍Go语言中...

跟我一起学习go语言(五)golang中结构体的初始化方法

1、自定义一个结构体typeVertexstruct{X,Yfloat64}2、初始化方法-指针:rect1:=new(Vertex)rect2:=&Vertex...

Go复合数据类型:结构体

一种通用的、对实体对象进行聚合抽象的能力,在Go中,提供这种聚合抽象能力的类型是结构体类型,也就是struct。自定义一个新类型在Go中,我们自定义一个新类型一般有两种方法。第一种是类型定义...

Go语言基础:方法

导读在阅读本文章前,假定你具备如下能力:?已掌握结构体1.方法1.1方法的概念在理解程序中方法的概念时,我们先看看现实中的一些情况,这样相对比较好理解一些。在农村的朋友可能会知道,在医疗落后的情况...

为什么 Go 语言 struct 要使用 tags

在Go语言中,struct是一种常见的数据类型,它可以用来表示复杂的数据结构。在struct中,我们可以定义多个字段,每个字段可以有不同的类型和名称。除了这些基本信息之外,Go还提供了s...

一文带你掌握掌握 Golang结构体与方法

1.Golang结构体的概念及定义结构体是Golang中一种复合类型,它是由一组具有相同或不同类型的数据字段组成的数据结构。结构体是一种用户自定义类型,它可以被用来封装多个字段,从而实现数据的...

取消回复欢迎 发表评论: