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

Java 9新特性,第1部分:新增Factory方法

gowuye 2024-04-03 16:13 9 浏览 0 评论

发现已添加到Java 9Collections Framework中的新的便利工厂方法

除了进一步的延迟之外,Java 9将于7月27日达到通用状态。其模块系统和Java Shell读取 - 评估 - 打印循环(REPL)工具正在受到相当大的关注,但是Java 9还提供了额外的增强功能,将使此版本难以忘怀。

我创建了一系列的帖子,探讨其他一些新的增强功能。这个系列试图回答你至少有一些关于这些产品的问题。我们将着重于添加到Java Collections Framework中的各种接口的新的便捷工厂方法。

编译并运行系列代码

我正在使用JDK 9早期访问版本的build 154编译并运行本系列中的所有代码。

方便工厂采集方法

Java增强建议(JEP)269:集合的便利工厂方法定义了几种工厂方法,用于方便地创建具有少量元素的不可修改集合和映射的实例。本节介绍这些方法后,为什么它们是必要的。

需要方便的工厂方法

Java经常被批评为冗长。例如,创建一个小的,不可修改的集合(例如,列表)涉及构造它,将其引用存储在局部变量中,add()通过引用多次调用,最后包装集合以获得不可修改的视图。请考虑以下示例:

List<String> list = new ArrayList<>();

list.add("a");

list.add("b");

list.add("c");

list = Collections.unmodifiableList(list);

这个详细示例不能简化为单个表达式,这意味着静态(不可更改)集合必须在静态初始化程序块中填充,而不是通过更方便的字段表达式。但是,有一些替代方法可以指定单个表达式:

List<String> list1 =

Collections.unmodifiableList(new ArrayList<>(Arrays.asList("a", "b", "c")));

List<String> list2 =

Collections.unmodifiableList(new ArrayList<String>() {{ add("a"); add("b"); add("c"); }});

List<String> list3 =

Collections.unmodifiableList(Stream.of("a", "b", "c").collect(toList()));

第一行java.util.List从另一个填充List,从中返回java.util.Arrays.asList()。该示例仍然有些冗长,需要List创建第二个对象(最终是垃圾回收),并且创建第二个对象List可能不是很明显的。

第二行使用一个匿名内部类的实例初始化器构造来实现减少的冗长度。然而,这种技术是非常模糊的,并且在每次使用时花费额外的课程。它还包含对包围实例和任何捕获对象的隐藏引用。最终,可能会发生内存泄漏和/或序列化问题。

第三行使用Java 8的Streams API来实现所需的结果。虽然较少冗长,但它涉及到一定量的不必要的对象创建和计算。此外,Streams不能以这种方式用于构造java.util.Map,除非可以从密钥计算值,或者流元素包含键和值。

这些潜在解决方案的问题导致引入了JEP 186:集合文字,它主张将集合文字添加到Java语言。甲文字集合是一个句法表达式,其值的阵列,List,Map,或其它聚合类型。请考虑以下原始示例的简明表示:

List<String> list = #[ "a", "b", "c" ];

没有新的语言功能像人们可能想象的一样简单或干净,这就是为什么收集文字没有添加到Java 9.相反,Java 9提供了工厂方法,为创建小型不可修改的收集/映射实例提供了很多好处,但与改变语言相比,成本和风险大大降低。

探索工厂方法

JEP 269的工厂方法受到类java.util.Collections和java.util.EnumSet类的工厂方法的启发。Collections提供用于创建空Lists,java.util.Sets和Maps的工厂方法,以及创建具有正好一个元素或键值对的单例Lists,Sets和Maps。EnumSet提供了几个重载的of(...)工厂方法,它们采用固定或可变数量的参数,以便方便地EnumSet使用指定的元素创建。Java 9模型EnumSet的of()方法提供一致和通用的方式来创建包含任意类型对象的Lists,Sets和Maps。

以下工厂方法已添加到List界面中:

static <E> List<E> of()

static <E> List<E> of(E e1)

static <E> List<E> of(E e1, E e2)

static <E> List<E> of(E e1, E e2, E e3)

static <E> List<E> of(E e1, E e2, E e3, E e4)

static <E> List<E> of(E e1, E e2, E e3, E e4, E e5)

static <E> List<E> of(E e1, E e2, E e3, E e4, E e5, E e6)

static <E> List<E> of(E e1, E e2, E e3, E e4, E e5, E e6, E e7)

static <E> List<E> of(E e1, E e2, E e3, E e4, E e5, E e6, E e7, E e8)

static <E> List<E> of(E e1, E e2, E e3, E e4, E e5, E e6, E e7, E e8, E e9)

static <E> List<E> of(E e1, E e2, E e3, E e4, E e5, E e6, E e7, E e8, E e9, E e10)

static <E> List<E> of(E... elements)

以下工厂方法已添加到Set界面中:

static <E> Set<E> of()

static <E> Set<E> of(E e1)

static <E> Set<E> of(E e1, E e2)

static <E> Set<E> of(E e1, E e2, E e3)

static <E> Set<E> of(E e1, E e2, E e3, E e4)

static <E> Set<E> of(E e1, E e2, E e3, E e4, E e5)

static <E> Set<E> of(E e1, E e2, E e3, E e4, E e5, E e6)

static <E> Set<E> of(E e1, E e2, E e3, E e4, E e5, E e6, E e7)

static <E> Set<E> of(E e1, E e2, E e3, E e4, E e5, E e6, E e7, E e8)

static <E> Set<E> of(E e1, E e2, E e3, E e4, E e5, E e6, E e7, E e8, E e9)

static <E> Set<E> of(E e1, E e2, E e3, E e4, E e5, E e6, E e7, E e8, E e9, E e10)

static <E> Set<E> of(E... elements)

在每个方法列表中,第一种方法创建一个空的不可修改的集合。接下来的10种方法可创建最多10个元素的不可修改集合。尽管他们的API混乱,但是这些方法避免了最终的varargs方法产生的数组分配,初始化和垃圾回收开销,这种方法支持任意大小的集合。

清单1演示List和Set工厂方法。

清单1.演示收集工厂方法

import java.util.List;

import java.util.Set;

public class ColDemo

{

public static void main(String[] args)

{

List<String> fruits = List.of("apple", "orange", "banana");

for (String fruit: fruits)

System.out.println(fruit);

try

{

fruits.add("pear");

}

catch (UnsupportedOperationException uoe)

{

System.err.println("unable to modify fruits list");

}

Set<String> marbles = Set.of("aggie", "alley", "steely");

for (String marble: marbles)

System.out.println(marble);

try

{

marbles.add("swirly");

}

catch (UnsupportedOperationException uoe)

{

System.err.println("unable to modify marbles set");

}

}

}

编译清单1如下:

javac ColDemo.java

运行生成的应用程序如下:

java ColDemo

我在一次运行中观察到以下输出:

apple

orange

banana

unable to modify fruits list

steely

alley

aggie

unable to modify marbles set

以下工厂方法已添加到Map界面中:

static <K,V> Map<K,V>

of()

static <K,V> Map<K,V>

of(K k1, V v1)

static <K,V> Map<K,V>

of(K k1, V v1, K k2, V v2)

static <K,V> Map<K,V>

of(K k1, V v1, K k2, V v2, K k3, V v3)

static <K,V> Map<K,V>

of(K k1, V v1, K k2, V v2, K k3, V v3, K k4, V v4)

static <K,V> Map<K,V>

of(K k1, V v1, K k2, V v2, K k3, V v3, K k4, V v4, K k5, V v5

static <K,V> Map<K,V>

of(K k1, V v1, K k2, V v2, K k3, V v3, K k4, V v4, K k5, V v5, K k6, V v6)

static <K,V> Map<K,V>

of(K k1, V v1, K k2, V v2, K k3, V v3, K k4, V v4, K k5, V v5, K k6, V v6, K k7, V v7

static <K,V> Map<K,V>

of(K k1, V v1, K k2, V v2, K k3, V v3, K k4, V v4, K k5, V v5, K k6, V v6, K k7, V v7,

K k8, V v8)

static <K,V> Map<K,V>

of(K k1, V v1, K k2, V v2, K k3, V v3, K k4, V v4, K k5, V v5, K k6, V v6, K k7, V v7,

K k8, V v8, K k9, V v9)

static <K,V> Map<K,V>

of(K k1, V v1, K k2, V v2, K k3, V v3, K k4, V v4, K k5, V v5, K k6, V v6, K k7, V v7,

K k8, V v8, K k9, V v9, K k10, V v10)

static <K,V> Map<K,V>

ofEntries(Map.Entry<? extends K,? extends V>... entries)

第一种方法创建一个空的不可修改的地图。接下来的10种方法可以创建最多10个键/值条目的不可修改的地图。这些方法增加了一些API杂乱,但是避免了由最终的varargs方法引起的数组分配,初始化和垃圾收集开销,支持任意大小的地图。

虽然varargs方法类似于等效的varargs方法for List和Set,它要求每个键值对被框。Map可以静态导入的以下新方法使方便的键和值到地图条目中:

Map.Entry<K,V> entry(K k, V v)

清单2演示Map的ofEntries()和entry()方法。

清单2.演示地图工厂方法

import java.util.Map;

import static java.util.Map.entry;

public class MapDemo

{

public static void main(String[] args)

{

Map<String, String> capCities =

Map.ofEntries(entry("Manitoba", "Winnipeg"),

entry("Alberta", "Edmonton"));

capCities.forEach((k, v) ->

System.out.printf("Key = %s, Value = %s%n", k, v));

try

{

capCities.put("British Columbia", "Victoria");

}

catch (UnsupportedOperationException uoe)

{

System.err.println("unable to modify capCities map");

}

}

}

编译清单2如下:

javac MapDemo.java

运行生成的应用程序如下:

java MapDemo

我在一次运行中观察到以下输出:

Key = Alberta, Value = Edmonton

Key = Manitoba, Value = Winnipeg

unable to modify capCities map

请注意,JDK的未来版本可能会通过使用值类型来减轻拳击费用。的entry()便利方法返回一个实现一个新导入的具体类型Map.Entry,以便于潜在未来迁移到值类型。

建筑细节

该Collections班为创建不可修改的包装方法ListS,SetS,和Map秒。这些方法不会产生固有的不可修改的集合/映射。而是采取另一个收集/地图,并将其包装在拒绝修改请求的类中,创建原始集合/映射的不可修改视图。拥有对基础集合/映射的引用仍然允许修改。每个包装器是一个额外的对象,需要与原始集合/映射相比的另一层次的间接和消耗更多的内存。最后,包装收藏/地图仍然承担支持突变的代价,即使它从来没有被修改。新的工厂方法不是这样。

提供用于创建小型,不可修改的集合/映射的工厂方法满足大量用例,并且有助于保持规范和实现简单。不可修改的集合/地图避免了制作防御性副本的需要,并且更适合并行处理。此外,小集合/映射占用的运行时空间很重要。java.util.HashSet使用Collections包装方法的两个元素的不可修改的直接创建将由六个对象组成:包装器,HashSet包含一个java.util.HashMap,其桶表(数组)和每个元素的一个节点实例。与存储的数据量相比,这是很多开销,并且对数据的访问不可避免地需要多个方法调用和指针解引用。小型,固定大小的集合的工厂方法避免了大部分这种开销,使用紧凑的基于现场或基于阵列的布局。不需要支持突变(并且在创建时知道收集/地图大小)也有助于节省空间。

这里有一些更多的细节:

·这些工厂返回的具体类不会公开为公共API。对于返回的集合/映射的运行时类型或身份不作任何保证,这样允许实现在不破坏兼容性的情况下随着时间的推移而改变。调用者唯一可以依赖的是返回的引用是其接口类型的实现。

·生成的对象是可序列化的。序列化代理对象被用作实现类的公共序列化形式。该代理可以防止有关具体实现的信息泄漏到序列化形式中,这样可以保留将来维护的灵活性,并允许具体实现从发行版更改为发布,而不会影响序列化兼容性。

·零元素,键和值不允许。(没有最近推出的集合/地图已经支持null。)此外,禁止空值为更紧凑的内部表示,更快的访问和更少的特殊情况提供了机会。

·因为这些List实现有望通过索引提供快速元素访问,它们实现java.util.RandomAccess标记接口。

·存储在这些集合/地图中的元素必须支持典型的集合/地图合同,包括适当的支持hashCode()和equals()。如果a Set或a 的元素以Map影响其hashCode()或equals()方法的方式突变,则收集/映射的行为可能会变得未指定。

·一旦构建并安全地发布,这些集合/映射实例将可以安全地在多个线程上下文中并发访问。

结论

Java 9即将推出,开发人员将需要了解其许多增强功能。这一系列的帖子试图通过探索除广泛预期的模块系统和REPL工具之外的各种增强功能来传达一些知识,这些功能在其他地方得到广泛的覆盖。

JEP 269的少量EnumSet工厂方法最大限度地减少Java 9不支持收集文字的痛苦。除了减少语法冗长度,这些工厂方法可以防止收集可变性的任何可能性,因此适用于基于Streams API或其他并行化环境。

相关推荐

爱上开源之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中一种复合类型,它是由一组具有相同或不同类型的数据字段组成的数据结构。结构体是一种用户自定义类型,它可以被用来封装多个字段,从而实现数据的...

取消回复欢迎 发表评论: