在 Groovy 与 Java 中创建和初始化映射

Java 和 Groovy 映射非常通用,允许键和值是扩展了 Object 类的任何类。
20 位读者喜欢这篇文章。
Business woman on laptop sitting in front of window

图片来源:Mapbox Uncharted ERG, CC-BY 3.0 US

我最近探索了在 创建和初始化列表 以及 在运行时构建列表 时 Java 和 Groovy 之间的一些差异。我观察到,与 Java 中所需的复杂性相比,Groovy 为这些目的提供了简单的工具。

在本文中,我将研究在 Java 和 Groovy 中创建和初始化映射。映射提供了开发可以通过搜索的结构的能力。如果找到了键,则返回与该键关联的。如今,映射已在许多编程语言中实现,包括 Java 和 Groovy,以及 Python(在 Python 中称为字典)、Perl、awk 和许多其他语言。另一个常用于描述映射的术语是关联数组,您可以在这篇维基百科文章中阅读相关内容。Java 和 Groovy 映射非常通用,允许键和值是扩展了 Object 类的任何类。

安装 Java 和 Groovy

Groovy 基于 Java,也需要安装 Java。您的 Linux 发行版的存储库中可能包含 Java 和 Groovy 的最新且不错的版本。或者,您可以按照上面链接中提到的说明安装 Groovy。对于 Linux 用户来说,一个不错的替代方案是 SDKMan,您可以使用它来获取多个版本的 Java、Groovy 和许多其他相关工具。在本文中,我使用的是 SDK 发布的

  • Java:OpenJDK 11 的 11.0.12-open 版本;
  • Groovy: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() 有两个重要的限制。首先,您以这种方式创建的映射实例是不可变的。其次,这种方式您最多可以提供 20 个参数,代表十个键值对。

尝试添加第十和第十一个对,例如“AG”、“安提瓜和巴布达”和“AR”、“阿根廷”,看看会发生什么。您将看到 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.of(),而是使用了 Map.ofEntries()

但是,假设我想创建一个映射实例并使用一些条目进行初始化,然后在以后添加到该映射中,我需要这样做

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 构造函数的参数,我创建了它的可变副本,然后我可以对其进行更改,例如使用 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(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 的字典,并且无需记住如果您有一个长度超过十对的初始列表所需的各种曲折。请注意,我们使用表达式

m1[“BY”] = “Belarus”

而不是 Java

m1.put(“BY”, “Belarus”)

此外,默认情况下,映射是可变的,这可以说是好是坏,具体取决于需求。我认为 Java 情况的“不可变默认”让我困扰的是,没有类似 Map.mutableOfMutableEntries() 的东西。这迫使刚刚弄清楚如何声明和初始化映射的程序员转换思路,并思考如何将他们拥有的不可变映射转换为可变映射。我也有些怀疑创建不可变的东西只是为了将其丢弃的意义。

另一个需要考虑的事情是,方括号作为键查找可以代替 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 特别适合编写脚本的好时机。通常,映射是脚本中的关键元素,提供查找表并通常充当内存数据库。我在此处使用的示例是 ISO 3166 双字符国家/地区代码和国家/地区名称的子集。任何访问世界各地国家/地区的互联网主机名的人都熟悉这些代码,这些代码可以构成脚本实用程序的一个有用部分,该实用程序查看日志文件中的互联网主机名以了解用户的地理分布。

Groovy 资源

Apache Groovy 站点 有很多很棒的文档。Mr. Haki 是另一个很棒的 Groovy 资源。Baeldung 站点 提供了大量关于 Java 和 Groovy 的实用指南。学习 Groovy 的一个真正重要原因是为了继续学习 Grails,这是一个非常高效的全栈 Web 框架,构建于 Hibernate、Spring Boot 和 Micronaut 等优秀组件之上。

标签
Chris Hermansen portrait Temuco Chile
自从 1978 年毕业于不列颠哥伦比亚大学以来,我几乎一直与某种计算机为伴。自 2005 年以来,我一直是全职 Linux 用户,从 1986 年到 2005 年一直是全职 Solaris 和 SunOS 用户,在此之前是 UNIX System V 用户。

评论已关闭。

Creative Commons License本作品根据 Creative Commons Attribution-Share Alike 4.0 International License 获得许可。
© . All rights reserved.