分别在Groovy和Java中创建并初始化映射的不同分析
作者:Chris 发布时间:2023-11-26 08:15:42
Java 和 Groovy 中的映射map都是非常通用的,它允许关键字key和值value为任意类型,只要继承了 Object 类即可。
我最近在探索 Java 与 Groovy 在 创建并初始化列表List 和 在运行时构建列表List 方面的一些差异。我观察到,就实现这些功能而言,Groovy 的简洁和 Java 的繁复形成了鲜明对比。
在这篇文章中,我将实现在 Java 和 Groovy 中创建并初始化映射Map。映射为开发支持根据 关键字key 检索的结构提供了可能,如果找到了这样一个关键字,它就会返回对应的 值value。今天,很多编程语言都实现了映射,其中包括 Java 和 Groovy,也包括了 Python(它将映射称为 字典dict)、Perl、awk 以及许多其他语言。另一个经常被用来描述映射的术语是 关联数组associative array,你可以在 百科文章 中了解更多。Java 和 Groovy 中的映射都是非常通用的,它允许关键字和值为任意类型,只要继承了 Object
类即可。
安装 Java 和 Groovy
Groovy 基于 Java,因此你需要先安装 Java。你的 Linux 发行版的仓库中可能有最近的比较好的 Java 和 Groovy 版本。或者,你也可以在根据上面链接中的指示来安装 Groovy。对于 Linux 用户来说,SDKMan 是一个不错的代替选项,你可以使用它来获取多个 Java 和 Groovy 版本,以及许多其他的相关工具。在这篇文章中,我使用的 SDK 发行版是:
Java: version 11.0.12-open of OpenJDK 11;
Groovy: version 3.0.8.
言归正传
Java 提供了非常多的方式来实例化和初始化映射,并且从 Java 9 之后,添加了一些新的方式。其中最明显的方式就是使用 java.util.Map.of()
这个静态方法,下面介绍如何使用它:
var m1 = Map.of(
"AF", "Afghanistan",
"AX", "Åland Islands",
"AL", "Albania",
"DZ", "Algeria",
"AS", "American Samoa",
"AD", "Andorra",
"AO", "Angola",
"AI", "Anguilla",
"AQ", "Antarctica");
System.out.println("m1 = " + m1);
System.out.println("m1 is an instance of " + m1.getClass());
事实证明,在此种情况下,Map.of()
有两个重要的限制。其一,这样创建出来的映射实例是不可变的immutable。其二,你最多只能提供 20 个参数,用来表示 10 个键值对key-value pair。
你可以尝试着添加第 10 对和第 11 对,比方说 "AG", "Antigua and Barbuda" 和 "AR", "Argentina",然后观察会发生什么。你将发现 Java 编译器尝试寻找一个支持 11 个键值对的 Map.of()
方法而遭遇失败。
快速查看 java.util.Map 类的文档,你就会找到上述第二个限制的原因,以及解决这个难题的一种方式:
var m2 = Map.ofEntries(
Map.entry("AF", "Afghanistan"),
Map.entry("AX", "Åland Islands"),
Map.entry("AL", "Albania"),
Map.entry("DZ", "Algeria"),
Map.entry("AS", "American Samoa"),
Map.entry("AD", "Andorra"),
Map.entry("AO", "Angola"),
Map.entry("AI", "Anguilla"),
Map.entry("AQ", "Antarctica"),
Map.entry("AG", "Antigua and Barbuda"),
Map.entry("AR", "Argentina"),
Map.entry("AM", "Armenia"),
Map.entry("AW", "Aruba"),
Map.entry("AU", "Australia"),
Map.entry("AT", "Austria"),
Map.entry("AZ", "Azerbaijan"),
Map.entry("BS", "Bahamas"),
Map.entry("BH", "Bahrain"),
Map.entry("BD", "Bangladesh"),
Map.entry("BB", "Barbados")
);
System.out.println("m2 = " + m2);
System.out.println("m2 is an instance of " + m2.getClass());
这就是一个比较好的解决方式,前提是我不在随后的代码里改变使用 Map.ofEntries()
创建并初始化的映射内容。注意,我在上面使用了 Map.ofEntries()
来代替 Map.of()
。
然而,假设我想要创建并初始化一个非空的映射,随后往这个映射中添加数据,我需要这样做:
var m3 = new HashMap<String,String>(Map.ofEntries(
Map.entry("AF", "Afghanistan"),
Map.entry("AX", "Åland Islands"),
Map.entry("AL", "Albania"),
Map.entry("DZ", "Algeria"),
Map.entry("AS", "American Samoa"),
Map.entry("AD", "Andorra"),
Map.entry("AO", "Angola"),
Map.entry("AI", "Anguilla"),
Map.entry("AQ", "Antarctica"),
Map.entry("AG", "Antigua and Barbuda"),
Map.entry("AR", "Argentina"),
Map.entry("AM", "Armenia"),
Map.entry("AW", "Aruba"),
Map.entry("AU", "Australia"),
Map.entry("AT", "Austria"),
Map.entry("AZ", "Azerbaijan"),
Map.entry("BS", "Bahamas"),
Map.entry("BH", "Bahrain"),
Map.entry("BD", "Bangladesh"),
Map.entry("BB", "Barbados")
));
System.out.println("m3 = " + m3);
System.out.println("m3 is an instance of " + m3.getClass());
m3.put("BY", "Belarus");
System.out.println("BY: " + m3.get("BY"));
这里,我把使用 Map.ofEntries()
创建出来的不可变映射作为 HashMap
的一个构造参数,以此创建了该映射的一个可变副本mutable copy,之后我就可以修改它 —— 比如使用 put()
方法。
让我们来看看上述过程如何用 Groovy 来实现:
def m1 = [
"AF": "Afghanistan",
"AX": "Åland Islands",
"AL": "Albania",
"DZ": "Algeria",
"AS": "American Samoa",
"AD": "Andorra",
"AO": "Angola",
"AI": "Anguilla",
"AQ": "Antarctica",
"AG": "Antigua and Barbuda",
"AR": "Argentina",
"AM": "Armenia",
"AW": "Aruba",
"AU": "Australia",
"AT": "Austria",
"AZ": "Azerbaijan",
"BS": "Bahamas",
"BH": "Bahrain",
"BD": "Bangladesh",
"BB": "Barbados"]
println "m1 = $m1"
println "m1 is an instance of ${m1.getClass()}"
m1["BY"] = "Belarus"
println "m1 = $m1"
只看一眼,你就会发现 Groovy 使用了 def
关键字而不是 var
—— 尽管在最近模型late-model的 Groovy(version 3+)中,使用 var
关键字也是可行的。
你还会发现,你是通过在括号里添加了一个键值对列表来创建一个映射的。不仅如此,这样创建的列表对象还非常有用,这里有几个原因。其一,它是可变的;其二,它是一个 LinkedHashMap
的实例,内部维持了数据的插入顺序。所以,当你运行 Java 版本的代码并打印出变量 m3
,你会看到:
m3 = {BB=Barbados, BD=Bangladesh, AD=Andorra, AF=Afghanistan, AG=Antigua and Barbuda, BH=Bahrain, AI=Anguilla, AL=Albania, AM=Armenia, AO=Angola, AQ=Antarctica, BS=Bahamas, AR=Argentina, AS=American Samoa, AT=Austria, AU=Australia, DZ=Algeria, AW=Aruba, AX=Åland Islands, AZ=Azerbaijan}
而当你运行 Groovy 版本的代码,你会看到:
m1 = [AF:Afghanistan, AX:Åland Islands, AL:Albania, DZ:Algeria, AS:American Samoa, AD:Andorra, AO:Angola, AI:Anguilla, AQ:Antarctica, AG:Antigua and Barbuda, AR:Argentina, AM:Armenia, AW:Aruba, AU:Australia, AT:Austria, AZ:Azerbaijan, BS:Bahamas, BH:Bahrain, BD:Bangladesh, BB:Barbados]
再一次,你将看到 Groovy 是如何简化事情的。这样的语法非常直观,有点像 Python 里的字典,并且,即使你有一个超过 10 个键值对的初始列表,你也不需要去记住各种必要的别扭方式。注意我们使用的表达式:
m1[“BY”] = “Belarus”
而在 Java 中,你需要这样做:
m1.put(“BY”, “Belarus”)
还有,这个映射默认是可变的,这么做的利弊很难评判,还是得取决于你的需求是什么。我个人觉得,Java 在这种情况下的 “默认不可变” 机制,最让我困扰的地方是,它没有一个类似于 Map.mutableOfMutableEntries()
的方法。这迫使一些刚学会如何声明和初始化一个映射的程序员,不得不转念去思考该如何把他们手中不可变的映射,转换为可变的。同时我也想问,创建一个不可变的对象然后再舍弃它,这样真的好吗?
另一个值得考虑的事情是,Groovy 使用方括号代替 Java 中的 put()
和 get()
方法来进行关键字查找。因此你可以这样写:
m1[“ZZ”] = m1[“BY”]
而不需要这样写:
m1.put(“ZZ”,m1.get(“BY”)
有时候,就像使用某个类的实例变量一样来使用映射中的关键字和值是一个好办法。设想你现在有一堆想要设置的属性,在 Groovy 中,它们看起来就像下面这样:
def properties = [
verbose: true,
debug: false,
logging: false]
然后,你可以改变其中的某个属性,就像下面这样:
properties.verbose = false
之所以这样能工作,是因为,只要关键字符合特定的规则,你就可以省略引号,然后直接用点操作符来代替方括号。尽管这个功能非常有用,也非常好用,它也同时也意味着,如果你要把一个变量作为一个映射的关键字来使用,你就必须把这个变量包裹在圆括号里,就像下面这样
def myMap = [(k1): v1, (k2): v2]
是时候告诉勤奋的读者 Groovy 是一门为编写脚本而量身定制的语言了。映射通常是脚本中的关键元素,它为脚本提供了查找表lookup table,并且通常起到了作为内存数据库的作用。我在这里使用的例子是 ISO 3166 规定的两个字母的国家代码和国家名称。对在世界上各个国家的互联网使用者来说,这些代码是很熟悉的。此外,假设我们要编写一个从日志文件中查找互联网主机名,并借此来了解用户的地理位置分布的脚本工具,那么这些代码会是十分有用的部分。
Groovy 相关资源
Apache Groovy 网站 上有非常多的文档。另一个很棒的 Groovy 资源是 Mr. Haki。Baeldung 网站 提供了大量 Java 和 Groovy 的有用教程。学习 Groovy 还有一个很棒的原因,那就是可以接着学习 Grails,后者是一个优秀的、高效率的全栈 Web 框架。它基于许多优秀组件构建而成,比如有 Hibernate、Spring Boot 和 Micronaut 等。
via: https://opensource.com/article/22/3/maps-groovy-vs-java
来源:https://linux.cn/article-14378-1.html


猜你喜欢
- Android Handler移除Message详解问题:1.removeMessage(what)函数是否只能移除对应what值的Mess
- 基本使用使用WebView通常是需要网络的,所以需要加上访问网络的权限<uses-permission android:name=&q
- 一、介绍JUnit是一款优秀的开源Java单元测试框架,也是目前使用率最高最流行的测试框架,开发工具Eclipse和IDEA对JUnit都有
- TimeSpan结构:表示一个时间间隔。它含有以下四个构造函数:TimeSpan(Int64)将 TimeSpan结构的新实例初始
- Java中提供了大数字(超过16位有效位)的操作类,即 java.math.BinInteger 类和 java.math.BigDecim
- 工作原理:Spring Cloud框架下的服务发现Eureka包含两个组件分别是: Eureka Server与Eureka ClientE
- 排序二叉树概念二叉排序树(Binary Sort Tree),又称二叉查找树(Binary Search Tree),亦称二叉搜索树。是数据
- JPA JpaRepository接口的使用SpringData的所有接口CrudRepository接口 ,其中提供了这些方法提供使用,同
- 前言Mybatis作为一个应用广泛的优秀的ORM框架,已经成了JavaWeb世界近乎标配的部分,这个框架具有强大的灵活性,在四大组件(Exe
- 引言在Google I/O 2014上,Google公布了Android L Preview版本,此版本的UI有了非常大的改变,很炫很给力!
- 问题换了台开发机,重新安装了下开发环境。突然发现Visual Studio Code无法用来调试Unity了。明明流程都是按照Unity官方
- 本文以一个实例的形式讲述了C#实现复杂XML的序列化与反序列化的方法。分享给大家供大家参考。具体方法如下:已知.xml(再此命名defaul
- 本文实例讲述了C#获取CPU编号的方法。分享给大家供大家参考。具体如下:/// <summary>/// Gets the cp
- 在上篇文章给大家介绍了FastDFS安装和配置整合Nginx-1.13.3的方法,大家可以点击查看下。今天使用Java代码实现文件的上传和下
- 一、登录认证基于过滤器链Spring Security的登录验证流程核心就是过滤器链。当一个请求到达时按照过滤器链的顺序依次进行处理,通过所
- 本文实例为大家分享了Android CameraManager类的具体代码,供大家参考,具体内容如下先看代码: private
- 新建线程新建线程很简单。只需要使用new关键字创建一个线程对象,然后调用它的start()启动线程即可。Thread thread1 = n
- 数组是一种数据结构,其声明方式如下:type[] arrayName;数组具有以下属性: 1.数组可以是
- 本文实例讲述了Java实现的简单音乐播放器功能。分享给大家供大家参考,具体如下:应用名称:Java简单的音乐播放器用到的知识:Java GU
- 我个人是比较喜欢逛贴吧的,贴吧里总是会有很多搞笑的动态图片,经常看一看就会感觉欢乐很多,可以释放掉不少平时的压力。确实,比起一张单调的图片,