This post is translated by lkxed.
12 KiB
在 Groovy 和 Java 中创建并初始化 Map 的不同
Java 和 Groovy 中的 Map 都是非常通用的,它允许关键字和值为任意类型,只要继承了 Object 类即可。
我最近在探索 Java 与 Groovy 在 创建并初始化列表 和 在运行时构建列表 放面的一些差异。我观察到,就实现这些功能而言,Groovy 的简洁和 Java 的繁复形成了鲜明对比。
在这篇文章中,我将实现在 Java 和 Groovy 中创建并初始化 Map。Map 为开发支持根据 关键字 检索的结构提供了可能,如果找到了这样一个关键字,它就会返回对应的 值。今天,很多编程语言都实现了 Map,其中包括 Java 和 Groovy,也包括了 Python(它将 Map 称为字典)、Perl、awk 以及许多其他语言。另一个经常被用来描述 Map 的术语是 关联数组,你可以在 这篇维基百科文章 中了解更多。Java 和 Groovy 中的 Map 都是非常通用的,它允许关键字和值为任意类型,只要继承了 Object 类即可。
安装 Java 和 Groovy
Groovy 基于 Java,因此你需要先安装 Java。你的 Linux 发行版的仓库中可能有最近的比较好的 Java 版本。或者,你也可以在根据上面链接中的指示来安装 Groovy。对于 Linux 用户来说,SDKMan 是一个不错的代替选项,你可以使用它来获取多个 Java 和 Groovy 版本,以及许多其他的相关工具。在这篇文章中,我使用的 SDK 发行版是:
- Java: version 11.0.12-open of OpenJDK 11;
- Groovy: version 3.0.8.
言归正传
Java 提供了非常多的方式来实例化和初始化 Map,并且从 Java 9 之后,一些新的方式也添加了进来。其中最明显的方式就是使用 java.util.Map.of()
这个静态方法,下面介绍如何使用它:
var m1 = [Map][6].of(
"AF", "Afghanistan",
"AX", "Åland Islands",
"AL", "Albania",
"DZ", "Algeria",
"AS", "American Samoa",
"AD", "Andorra",
"AO", "Angola",
"AI", "Anguilla",
"AQ", "Antarctica");
[System][7].out.println("m1 = " + m1);
[System][7].out.println("m1 is an instance of " + m1.getClass());
事实证明,在此种情况下,Map.of()
有两个重要的限制。其一,这样创建出来的 Map 实例是不可变的。其二,你最多只能提供 20 个参数,用来表示 10 个键值对。
你可以尝试着添加第 10 对和第 11 对,比方说 "AG", "Antigua and Barbuda" 和 "AR", "Argentina",然后观察会发生什么。你将发现 Java 编译器尝试寻找一个支持 11 个键值对的 Map.of()
方法。
快速查看 java.util.Map 类的文档,你就会找到上述第二个限制的原因,以及解决这个难题的一种方式:
var m2 = [Map][6].ofEntries(
[Map][6].entry("AF", "Afghanistan"),
[Map][6].entry("AX", "Åland Islands"),
[Map][6].entry("AL", "Albania"),
[Map][6].entry("DZ", "Algeria"),
[Map][6].entry("AS", "American Samoa"),
[Map][6].entry("AD", "Andorra"),
[Map][6].entry("AO", "Angola"),
[Map][6].entry("AI", "Anguilla"),
[Map][6].entry("AQ", "Antarctica"),
[Map][6].entry("AG", "Antigua and Barbuda"),
[Map][6].entry("AR", "Argentina"),
[Map][6].entry("AM", "Armenia"),
[Map][6].entry("AW", "Aruba"),
[Map][6].entry("AU", "Australia"),
[Map][6].entry("AT", "Austria"),
[Map][6].entry("AZ", "Azerbaijan"),
[Map][6].entry("BS", "Bahamas"),
[Map][6].entry("BH", "Bahrain"),
[Map][6].entry("BD", "Bangladesh"),
[Map][6].entry("BB", "Barbados")
);
[System][7].out.println("m2 = " + m2);
[System][7].out.println("m2 is an instance of " + m2.getClass());
这就是一个比较好的解决方式,前提是我不在随后的代码里改变使用 Map.ofEntries()
创建并初始化的 Map 内容。注意,我在上面使用了 Map.ofEntries()
来代替 Map.of()
。
然而,假设我想要创建并初始化一个非空的 Map,随后往这个 Map 中添加数据,我需要这样做:
var m3 = new HashMap<[String][9],String>([Map][6].ofEntries(
[Map][6].entry("AF", "Afghanistan"),
[Map][6].entry("AX", "Åland Islands"),
[Map][6].entry("AL", "Albania"),
[Map][6].entry("DZ", "Algeria"),
[Map][6].entry("AS", "American Samoa"),
[Map][6].entry("AD", "Andorra"),
[Map][6].entry("AO", "Angola"),
[Map][6].entry("AI", "Anguilla"),
[Map][6].entry("AQ", "Antarctica"),
[Map][6].entry("AG", "Antigua and Barbuda"),
[Map][6].entry("AR", "Argentina"),
[Map][6].entry("AM", "Armenia"),
[Map][6].entry("AW", "Aruba"),
[Map][6].entry("AU", "Australia"),
[Map][6].entry("AT", "Austria"),
[Map][6].entry("AZ", "Azerbaijan"),
[Map][6].entry("BS", "Bahamas"),
[Map][6].entry("BH", "Bahrain"),
[Map][6].entry("BD", "Bangladesh"),
[Map][6].entry("BB", "Barbados")
));
[System][7].out.println("m3 = " + m3);
[System][7].out.println("m3 is an instance of " + m3.getClass());
m3.put("BY", "Belarus");
[System][7].out.println("BY: " + m3.get("BY"));
这里,我把使用 Map.ofEntries()
创建出来的不可变 Map 作为 HashMap
的一个构造参数,以此创建了该 Map 的一个可变拷贝,之后我就可以修改它 —— 比如使用 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
—— 尽管在最近模型的 Groovy(version 3+)中,使用 var
关键字也是可行的。
你还会发现,你是通过在括号里添加了一个键值对列表来创建一个 Map 的。不仅如此,这样创建的列表对象还非常有用,这里有几个原因。其一,它是可变的;其二,它是一个 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”)`
还有,这个 Map 默认是可变的,这么做的利弊很难评判,还是得取决于你的需求是什么。我个人觉得,Java 在这种情况下的 “默认不可变” 机制,最让我困扰的地方是,它没有一个类似于 Map.mutableOfMutableEntries()
的方法。这迫使一些刚学会如何声明和初始化一个 Map 的程序员,不得不转念去思考该如何把他们手中不可变的 Map,转换为可变的。同时我也想问,创建一个不可变的对象然后再舍弃它,这样真的好吗?
另一个值得考虑的事情是,Groovy 使用方括号代替 Java 中的 put()
和 get()
方法来进行关键字查找。因此你可以这样写:
`m1[“ZZ”] = m1[“BY”]`
而不需要这样写:
`m1.put(“ZZ”,m1.get(“BY”))`
有时候,就像使用某个类的实例变量一样来使用 Map 中的关键字和值是一个好办法。设想你现在有一堆想要设置的属性,在 Groovy 中,它们看起来就像下面这样:
def properties = [
verbose: true,
debug: false,
logging: false]
然后,你可以改变其中的某个属性,就像下面这样:
`properties.verbose = false`
之所以这样能工作,是因为,只要关键字符合特定的规则,你就可以省略引号,然后直接用点操作符来代替方括号。尽管这个功能非常有用,也非常好用,它也同时也意味着,如果你要把一个变量作为一个 Map 的关键字来使用,你就必须把这个变量包裹在圆括号里,就像下面这样:
`def myMap = [(k1): v1, (k2): v2]`
是时候告诉勤奋的读者 Groovy 是一门为编写脚本而量身定制的语言了。Map 通常是脚本中的关键元素,它为脚本提供了查找表,并且通常起到了作为内存数据库的作用。我在这里使用的例子是 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
作者:Chris Hermansen 选题:lujun9972 译者:lkxed 校对:校对者ID