分别在Groovy和Java中创建并初始化映射的不同分析

目录
  • 安装 Java 和 Groovy
  • Groovy 相关资源

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. HakiBaeldung 网站 提供了大量 Java 和 Groovy 的有用教程。学习 Groovy 还有一个很棒的原因,那就是可以接着学习 Grails,后者是一个优秀的、高效率的全栈 Web 框架。它基于许多优秀组件构建而成,比如有 Hibernate、Spring Boot 和 Micronaut 等。

via: https://opensource.com/article/22/3/maps-groovy-vs-java

以上就是 分别在Groovy和Java中创建并初始化映射的不同分析的详细内容,更多关于 Groovy和Java中创建初始化映射的资料请关注我们其它相关文章!

(0)

相关推荐

  • 深入学习java中的Groovy 和 Scala 类

    前言 Java 传承的是平台,而不是语言.有超过 200 种语言可以在 JVM 上运行,它们之中不可避免地会有一种语言最终将取代 Java 语言,成为编写 JVM 程序的最佳方式.本系列将探讨三种下一代 JVM 语言:Groovy.Scala 和 Clojure,比较并对比新的功能和范例,让 Java 开发人员对自己近期的未来发展有大体的认识. Java 语言的开发人员精通 C++ 和其他语言,包括多继承(multiple inheritance),使得类可以继承自任意数量的父类.多继承带来的一

  • Java中的Gradle与Groovy的区别及存在的关系

    目录 一.Gradle构建的利与弊 二.Groovy的优点 三.依存关系 四.认识build.gradle 五.Gradlebuildscript 六.什么是闭包 七.Gradle只是闭包 八.探索Gradle依赖项配置 九.打包Gradle版本 十.任务 前言: 在Java项目中,有两个主要的构建系统:Gradle和Maven.构建系统主要管理潜在的复杂依赖关系并正确编译项目.还可以将已编译的项目以及所有资源和源文件打包到.war或.jar文件中.对于简单的构建,Maven和Gradle之间的

  • Java动态脚本Groovy

    目录 1.Groovy特性 2.核心涉及 3.Java与Groovy转换 第一步:引入Groovy依赖 第二步:创建interface接口声明方法 第三步:在resources目录下创建.groovy文件 第四步:创建Groovy脚本装载类,动态解析脚本为Class 第五步:读取脚本内容,执行脚本 4.Groovy特性验证 第一步:将之前Groovy脚本数据修改.存于数据库表中,动态加载脚本 第二步:数据库表中:添加.查询Groovy脚本,动态加载执行 第三步:多次修改表数据值,查看执行结果 5

  • 详解Java执行groovy脚本的两种方式

    记录Java执行groovy脚本的两种方式,简单粗暴: 一种是通过脚本引擎ScriptEngine提供的eval(String)方法执行脚本内容:一种是执行groovy脚本: 二者都通过Invocable来传递参数并获取执行结果: Invocable:脚本引擎的解释器接口,提供invokeFunction和invokeMethod两种传递参数并获取执行结果的方法,Java JDK API文档解释如下: invokeFunction: invokeMethod: 以下为案例: 引入依赖 <depe

  • 分别在Groovy和Java中创建并初始化映射的不同分析

    目录 安装 Java 和 Groovy Groovy 相关资源 Java 和 Groovy 中的映射map都是非常通用的,它允许关键字key和值value为任意类型,只要继承了 Object 类即可. 我最近在探索 Java 与 Groovy 在 创建并初始化列表List 和 在运行时构建列表List 方面的一些差异.我观察到,就实现这些功能而言,Groovy 的简洁和 Java 的繁复形成了鲜明对比. 在这篇文章中,我将实现在 Java 和 Groovy 中创建并初始化映射Map.映射为开发支

  • Java中创建ZIP文件的方法

    java创建zip文件的代码如下如下: import java.io.File; import java.io.FileInputStream; import java.io.FileOutputStream; import java.io.IOException; import java.util.zip.ZipEntry; import java.util.zip.ZipInputStream; import java.util.zip.ZipOutputStream; public cla

  • java中创建、写入文件的5种方式

    在java中有很多的方法可以创建文件写文件,你是否真的认真的总结过?下面笔者就帮大家总结一下java中创建文件的五种方法. Files.newBufferedWriter(Java 8) Files.write(Java 7 推荐) PrintWriter File.createNewFile FileOutputStream.write(byte[] b) 管道流 实际上不只这5种,通过管道流的排列组合,其实有更多种,但是笔者总结的这五种可以说是最常用及最佳实践, 前提小知识 以前我在写技术文

  • java中创建写入文件的6种方式详解与源码实例

    在java中有很多的方法可以创建文件写文件,你是否真的认真的总结过?下面笔者就帮大家总结一下java中创建文件的五种方法. Files.newBufferedWriter(Java 8) Files.write(Java 7 推荐) PrintWriter File.createNewFile FileOutputStream.write(byte[] b) 管道流 实际上不只这5种,通过管道流的排列组合,其实有更多种,但是笔者总结的这五种可以说是最常用及最佳实践,前提小知识 以前我在写技术文章

  • 实例解析Java中的构造器初始化

    1.初始化顺序 当Java创建一个对象时,系统先为该对象的所有实例属性分配内存(前提是该类已经被加载过了),接着程序开始对这些实例属性执行初始化,其初始化顺序是:先执行初始化块或声明属性时制定的初始值,再执行构造器里制定的初始值. 在类的内部,变量定义的先后顺序决定了初始化的顺序,即时变量散布于方法定义之间,它们仍就会在任何方法(包括构造器)被调用之前得到初始化. class Window { Window(int maker) { System.out.println("Window(&quo

  • Java 中的FileReader和FileWriter源码分析_动力节点Java学院整理

    FileReader和FileWriter源码分析 1. FileReader 源码(基于jdk1.7.40) package java.io; public class FileReader extends InputStreamReader { public FileReader(String fileName) throws FileNotFoundException { super(new FileInputStream(fil java io系列21之 InputStreamReade

  • Java中的InputStreamReader和OutputStreamWriter源码分析_动力节点Java学院整理

    InputStreamReader和OutputStreamWriter源码分析 1. InputStreamReader 源码(基于jdk1.7.40) package java.io; import java.nio.charset.Charset; import java.nio.charset.CharsetDecoder; import sun.nio.cs.StreamDecoder; // 将"字节输入流"转换成"字符输入流" public class

  • 详解Java中的实例初始化块(IIB)

    在 Java 语言中的类初始化块 文章中我们简单的介绍了下 Java 中的实例初始化块 ( IIB ).不过我觉得介绍的有点简单了,于是,再写一篇文章详细介绍下吧. Java 语言中,存在三种操作:方法 .构造函数 和 初始化块. 其中初始化块又分为 实例初始化块 ( IIB ) 和 静态初始化块.本章节,我们主要介绍实例初始化块. 实例初始化块 用于初始化实例变量. 实例初始化块 会在初始化类的一个实例时执行,而且在构造函数之前就执行.并且每次创建类的对象时它们都会执行. 实例化块的语法 实例

  • Java中@ConfigurationProperties实现自定义配置绑定问题分析

    目录 @ConfigurationProperties使用 @ConfigurationProperties特点 宽松绑定 支持复杂属性类型 激活@ConfigurationProperties 通过@EnableConfigurationProperties 通过@ConfigurationPropertiesScan @ConfigurationProperties与@Value对比 使用 Spring Boot Configuration Processor 完成自动补全 @Configu

  • Java中为什么ArrayList初始化容量大小为10

    目录 背景 为什么HashMap的初始化容量为16? ArrayList的初始化容量是10吗? 为什么ArrayList的初始化容量为10? 小结 背景 看ArrayList源码时,无意中看到ArrayList的初始化容量大小为10,这就奇怪了!我们都知道ArrayList和HashMap底层都是基于数组的,但为什么ArrayList不像用HashMap那样用16作为初始容量大小,而是采用10呢? 于是各方查找资料,求证了这个问题,这篇文章就给大家讲讲. 为什么HashMap的初始化容量为16?

随机推荐