Groovy 集合与闭包

御弟哥哥 等级 354 0 0

Groovy 集合

在 Groovy 提供的所有方便的快捷方式和功能中,最有帮助的一个可能就是内置的 集合。回想一下在 Java 编程中是如何使用集合的 — 导入 java.util 类,初始化集合,将项加入集合。这三个步骤都会增加不少代码。

而 Groovy 可以直接在语言内使用集合。在 Groovy 中,不需要导入专门的类,也不需要初始化对象。集合是语言本身的本地成员。Groovy 也使集合(或者列表)的操作变得非常容易,为增加和删除项提供了直观的帮助。

可以将范围当作集合

在前一节学习了如何用 Groovy 的范围将循环变得更容易。范围表达式 “0..4” 代表数字的集合— 0、1、2、3 和 4。为了验证这一点,请创建一个新类,将其命名为 Ranger。保留类定义和 main 方法定义。但是这次添加以下代码:

def range = 0..4
println range.class
assert range instanceof List

请注意,assert 命令用来证明范围是 java.util.List 的实例。接着运行这个代码,证实该范围现在是类型 List 的集合。

丰富的支持

Groovy 的集合支持相当丰富,而且美妙之处就在于,在 Groovy 的魔法背后,一切都是标准的 Java 对象。每个 Groovy 集合都是 java.util.Collection 或 java.util.Map 的实例。

前面提到过,Groovy 的语法提供了本地列表和映射。例如,请将以下两行代码添加到 Ranger 类中:

def coll = ["Groovy", "Java", "Ruby"]
assert  coll instanceof Collection
assert coll instanceof ArrayList

你将会注意到,coll 对象看起来很像 Java 语言中的数组。实际上,它是一个 Collection。要在普通的 Java 代码中得到集合的相同实例,必须执行以下操作:

Collection<String> coll = new ArrayList<String>();
coll.add("Groovy");
coll.add("Java");
coll.add("Ruby");

在 Java 代码中,必须使用 add() 方法向 ArrayList 实例添加项。

添加项

Groovy 提供了许多方法可以将项添加到列表 — 可以使用 add() 方法(因为底层的集合是一个普通的 ArrayList 类型),但是还有许多快捷方式可以使用。

例如,下面的每一行代码都会向底层集合加入一些项:

coll.add("Python")
coll << "Smalltalk"
coll[5] = "Perl"

请注意,Groovy 支持操作符重载 —<< 操作符被重载,以支持向集合添加项。还可以通过位置参数直接添加项。在这个示例中,由于集合中只有四个项,所以 [5] 操作符将 “Perl” 放在最后。请自行输出这个集合并查看效果。

检索非常轻松

如果需要从集合中得到某个特定项,可以通过像上面那样的位置参数获取项。例如,如果想得到第二个项 “Java”,可以编写下面这样的代码(请记住集合和数组都是从 0 开始):

assert coll[1] == "Java"

Groovy 还允许在集合中增加或去掉集合,如下所示:

def numbers = [1,2,3,4]
assert numbers + 5 == [1,2,3,4,5]
assert numbers - [2,3] == [1,4]

请注意,在上面的代码中, 实际上创建了新的 集合实例,由最后一行可以看出。

魔法方法

Groovy 还为集合添加了其他一些方便的功能。例如,可以在集合实例上调用特殊的方法,如下所示:

def numbers = [1,2,3,4]
assert numbers.join(",") == "1,2,3,4" 
assert [1,2,3,4,3].count(3) == 2

join() 和 count() 只是在任何项列表上都可以调用的众多方便方法中的两个。分布操作符(spread operator) 是个特别方便的工具,使用这个工具不用在集合上迭代,就能够调用集合的每个项上的方法。

假设有一个 String 列表,现在想将列表中的项目全部变成大写,可以编写以下代码:

assert ["JAVA", "GROOVY"] == 
  ["Java", "Groovy"]*.toUpperCase()

请注意 *. 标记。对于以上列表中的每个值,都会调用 toUpperCase(),生成的集合中每个 String 实例都是大写的。

Groovy 映射

除了丰富的列表处理功能,Groovy 还提供了坚固的映射机制。同列表一样,映射也是本地数据结构。而且 Groovy 中的任何映射机制在幕后都是 java.util.Map 的实例。

Java 语言中的映射

Java 语言中的映射是名称-值对的集合。所以,要用 Java 代码创建典型的映射,必须像下面这样操作:

Map<String, String>map = new HashMap<String, String>();
map.put("name", "Andy");
map.put("VPN-#","45");

一个 HashMap 实例容纳两个名称-值对,每一个都是 String 的实例。

通过 Groovy 进行映射

Groovy 使得处理映射的操作像处理列表一样简单 — 例如,可以用 Groovy 将上面的 Java 映射写成

def hash = [name:"Andy", "VPN-#":45]

请注意,Groovy 映射中的键不必是 String。在这个示例中,name 看起来像一个变量,但是在幕后,Groovy 会将它变成 String。

全都是 Java

接下来创建一个新类 Mapper 并加入上面的代码。然后添加以下代码,以证实底层的代码是真正的 Java 代码:

assert hash.getClass() == java.util.LinkedHashMap

可以看到 Groovy 使用了 Java 的 LinkedHashMap 类型,这意味着可以使用标准的 Java 一样语句对 hash 中的项执行 put 和 get 操作。

hash.put("id", 23)
assert hash.get("name") == "Andy"

有 groovy 特色的映射

现在您已经看到,Groovy 给任何语句都施加了魔法,所以可以用 . 符号将项放入映射中。如果想将新的名称-值对加入映射(例如 dob 和 “01/29/76”),可以像下面这样操作:

hash.dob = "01/29/76"

. 符号还可以用来获取项。例如,使用以下方法可以获取 dob 的值:

assert hash.dob == "01/29/76"

当然 . 要比调用 get() 方法更具 Groovy 特色。

位置映射

还可以使用假的位置语法将项放入映射,或者从映射获取项目,如下所示:

assert hash["name"] == "Andy"
hash["gender"] = "male"
assert hash.gender == "male"
assert hash["gender"] == "male"

但是,请注意,在使用 [] 语法从映射获取项时,必须将项作为 String 引用。

Groovy 中的闭包

现在,闭包是 Java 世界的一个重大主题,对于是否会在 Java 7 中包含闭包仍然存在热烈的争论。有些人会问:既然 Groovy 中已经存在闭包,为什么 Java 语言中还需要闭包?这一节将学习 Groovy 中的闭包。如果没有意外,在闭包成为 Java 语法的正式部分之后,这里学到的内容将给您带来方便。

不再需要更多迭代

虽然在前几节编写了不少集合代码,但还没有实际地在集合上迭代。当然,您知道 Groovy 就是 Java,所以如果愿意,那么总是能够得到 Java 的 Iterator 实例,用它在集合上迭代,就像下面这样:

def acoll = ["Groovy", "Java", "Ruby"]

for(Iterator iter = acoll.iterator(); iter.hasNext();){
 println iter.next()
}

实际上在 for 循环中并不需要类型声明,因为 Groovy 已经将迭代转变为任何集合的直接成员。在这个示例中,不必获取 Iterator 实例并直接操纵它,可以直接在集合上迭代。而且,通常放在循环构造内的行为(例如 for 循环体中 println)接下来要放在闭包内。在深入之前,先看看如何执行这步操作。

能否看见闭包? 对于上面的代码,可以用更简洁的方式对集合进行迭代,如下所示:

def acoll = ["Groovy", "Java", "Ruby"]

acoll.each{
 println it
}

请注意,each 直接在 acoll 实例内调用,而 acoll 实例的类型是 ArrayList。在 each 调用之后,引入了一种新的语法 —{,然后是一些代码,然后是 }。由 {} 包围起来的代码块就是闭包。

执行代码

闭包是可执行的代码块。它们不需要名称,可以在定义之后执行。所以,在上面的示例中,包含输出 it(后面将简单解释 it)的行为的无名闭包将会在 acoll 集合类型中的每个值上被调用。

在较高层面上,{} 中的代码会执行三次,从而生成如图 13 所示的输出。

图 13. 迭代从未像现在这样容易 Groovy 集合与闭包

闭包中的 it 变量是一个关键字,指向被调用的外部集合的每个值 — 它是默认值,可以用传递给闭包的参数覆盖它。下面的代码执行同样的操作,但使用自己的项变量:

def acoll = ["Groovy", "Java", "Ruby"]

acoll.each{ value ->
 println value
}

在这个示例中,用 value 代替了 Groovy 的默认 it。

迭代无处不在

闭包在 Groovy 中频繁出现,但是,通常用于在一系列值上迭代的时候。请记住,一系列值可以用多种方式表示,不仅可以用列表表示 — 例如,可以在映射、String、JDBC Rowset、File 的行上迭代,等等。

如果想在前面一节 “Groovy 中的映射” 中的 hash 对象上迭代,可以编写以下代码:

def hash = [name:"Andy", "VPN-#":45]
hash.each{ key, value ->
 println "${key} : ${value}"
}

请注意,闭包还允许使用多个参数 — 在这个示例中,上面的代码包含两个参数(key 和 value)。

使用 Java 代码迭代

以下是使用典型的 Java 构造如何进行同样的迭代:

Map<String, String>map = new HashMap<String, String>();
map.put("name", "Andy");
map.put("VPN-#","45");


for(Iterator iter = map.entrySet().iterator(); iter.hasNext();){
 Map.Entry entry = (Map.Entry)iter.next();
 System.out.println(entry.getKey() + " : " + entry.getValue());
}

上面的代码比 Groovy 的代码长得多,是不是?如果要处理大量集合,那么显然用 Groovy 处理会更方便。

迭代总结

请记住,凡是集合或一系列的内容,都可以使用下面这样的代码进行迭代。

"ITERATION".each{
 println it.toLowerCase()
}

闭包的更多使用方式

虽然在迭代上使用闭包的机会最多,但闭包确实还有其他用途。因为闭包是一个代码块,所以能够作为参数进行传递(Groovy 中的函数或方法不能这样做)。闭包在调用的时候才会执行这一事实(不是在定义的时候)使得它们在某些场合上特别有用。

例如,通过 Eclipse 创建一个 ClosureExample 对象,并保持它提供的默认类语法。在生成的 main() 方法中,添加以下代码:

def excite = { word ->
 return "${word}!!"
}

这段代码是名为 excite 的闭包。这个闭包接受一个参数(名为 word),返回的 String 是 word 变量加两个感叹号。请注意在 String 实例中替换 的用法。在 String 中使用 ${value}语法将告诉 Groovy 替换 String 中的某个变量的值。可以将这个语法当成 return word + "!!" 的快捷方式。

延迟执行

既然有了闭包,下面就该实际使用它了。可以通过两种方法调用闭包:直接调用或者通过 call() 方法调用。

继续使用 ClosureExample 类,在闭包定义下面添加以下两行代码:

assert "Groovy!!" == excite("Groovy")
assert "Java!!" == excite.call("Java")

可以看到,两种调用方式都能工作,但是直接调用的方法更简洁。不要忘记闭包在 Groovy 中也是一类对象 — 既可以作为参数传递,也可以放在以后执行。用普通的 Java 代码可以复制同样的行为,但是不太容易。现在不会感到惊讶了吧?

收藏
评论区

相关推荐

Gradle技术之一 Groovy语法精讲
Gradle技术之一 Groovy语法精讲 gradle脚本是基于groovy语言开发的,想要学好gradle必须先要对groovy有一个基本的认识 1. Groovy特点 groovy是一种DSL语言,所谓的DSL语言,就是专门针对某一特定领域的语言,专精而不专广 是一种基于JVM的开发语言,也是编译成class字节码文件 结合和Pytho
Gradle技术之四 - Gradle的Task详解
1 Gradle的Task详解 1 Task定义和配置 2 Task的执行 3 Task的依赖和执行顺序 4 Task类型 5 Task结合gradle的生命周期 6 Task实战 1.1 Task定义和配置 1.1.1 查看所有的task java ./gradlew tasks 输出 Task :tasks
半小时掌握Android Gradle
目前国内对Android领域的探索已经越来越深,不少技术领域如插件化、热修复、构建系统等都对Gradle有迫切的需求,不懂Gradle将无法完成上述事情。所以Gradle必须要学习。 Gradle 里的几乎任何东西都是基于这两个基础概念: task project 掌握了这两个,你就掌握了一大半的 Gradle 知识了。 首先讲 Task
Groovy初探
开始之前 了解本教程的主要内容,以及如何从中获得最大收获。 关于本教程 如果现在有人要开始完全重写 Java,那么 Groovy 就像是 Java 2.0。Groovy 并没有取代 Java,而是作为 Java 的补充,它提供了更简单、更灵活的语法,可以在运行时动态地进行类型检查。您可以使用 Groovy 随意编写 Java 应用程序,连接 Java
Groovy基础与循环语句
Groovy 入门 在这一节中,将真正开始进行 Groovy 编程。首先,学习如何轻松地安装 Groovy(通过 Eclipse Groovy 插件),然后从一些有助于了解 Groovy 的简单示例开始。 轻松安装 Groovy 为了迅速开始使用 Groovy,需要做的全部工作就是安装 Eclipse 的 Groovy 插件。打开 Ecliplse,在
Groovy 集合与闭包
Groovy 集合 在 Groovy 提供的所有方便的快捷方式和功能中,最有帮助的一个可能就是内置的 集合。回想一下在 Java 编程中是如何使用集合的 — 导入 java.util 类,初始化集合,将项加入集合。这三个步骤都会增加不少代码。 而 Groovy 可以直接在语言内使用集合。在 Groovy 中,不需要导入专门的类,也不需要初始化对象。集合是语
Groovy中的类
Groovy 中的类 迄今为止,您已经用 Groovy 输出了许多次 “Hello World”,已经操作了集合,用闭包在集合上迭代,也定义了您自己的闭包。做所有这些工作时,甚至还没有讨论那个对 Java 开发人员来说至关重要的概念 — 类。 当然,您已经在这个教程中使用过类了:您编写的最后几个示例就是在不同类的 main() 方法中。而且,您已经知道,在
彻底理解js闭包
在文章开头,我先放出MDN给出的定义: 闭包是指那些能够访问独立(自由)变量的函数 (变量在本地使用,但定义在一个封闭的作用域中)。换句话说,这些函数可以“记忆”它被创建时候的环境。 现在不需要看懂它,我会在第一个例子中解释清楚它的意思。让我们开始吧! 2018.3.20更新:现在MDN上的定义已经改为:"A closure is the comb
Python 中的闭包
闭包定义: 如果在一个内部函数里,对在外部作用于(但不是在全局作用域)的变量进行引用,那么内部函数就被认为是闭包 Python 中的闭包 原文出处: 田小计划(http://www.cnblogs.com/wilber2013/p/4658894.html) 闭包(closure)是函数式编程的重要的语法结构
swift - Alamofire 4.0
  在这前,已经介绍并学习过AFNetworking(http://www.cnblogs.com/chenxianming/p/5674652.html)源码了,现在通过阅读学习Alamofire最新源码同时,学习swift 3.0这门语言,虽然
JS核心原理理解闭包
前置概念在正式看闭包之前,我们先来学习一下前置知识,那就是JS中的作用域,我们知道,在ES5之中,作用域分为两种:全局作用域和函数作用域,随着ES6的到来,新增了块级作用域,想更好的理解闭包,那么搞清楚作用域是首要条件全局作用域我们知道,对于变量而言,我们一般会分成两类:全局变量和局部变量,一般定义在最外围环境的为全局变量,定义在函数当中的为局部变量,在we
JS - 从执行上下文的角度来理解闭包
今天看到一篇关于闭包的文章,里面有这样一句话 “就我而言对于闭包的理解仅止步于一些概念,看到相关代码知道这是个闭包,但闭包能解决哪些问题场景我了解的并不多”,这说的不就是我么,每每在面试中被问及什么是闭包,大部分情况下得到的答复是(至少我以前是)A函数嵌套B函数,B函数使用了A函数的内部变量,且A函数返回B函数,这就是闭包。而往往面试官想要听到的并不是这样的
重学JavaScript(函数)闭包
序言学习JavaScript切勿好高骛远。正所谓贪多嚼不烂,前端标准和工具这几年的飞速发展,以及不时冒出的“新鲜玩意”让众多前端从业者惊呼:“学不动啦学不动啦!学习速度跟不上技术发展速度!我感到手忙脚乱、力不从心……"如果你有以上“症状”,请勿着急,这不过是你内心不安造成的。你为何追新?你又何苦追新?在根基不牢的情况下,就算盖楼盖到18层,再往上堆一块砖,都
你不可不知的JS面试题(第三期)
1、什么是闭包?如图所示,闭包就是一个定义在函数内部的函数,其作用是将函数内部和函数外部连接起来。大家知道,作用域的问题,就是在函数内部定义的变量称为局部变量,外部取不到值。下面我们通过代码来更加详细地看一下: function A()        let x  1;        return function B()            c
小记面试题-2021
CSS类 1.解释浮动及其工作原理浮动的元素可以向左或者向右移动,直到它的外边缘碰到包含元素(父元素)或另一个浮动元素的边框为止。要想使元素浮动,必须为元素设置一个宽度。虽然浮动的元素已不在文档流中, 但是它浮动后所处的位置依然在浮动之前的水平方向上。因为浮动元素不在文档流中,所以文档流中的块元素表现的就像浮动元素不存在一样,下面的元素会填补原来的位置。有些