0%

Groovy之集合容器

这里对Groovy中常见的集合类型进行介绍

abstract.png

Array 数组

严格意义上来说,数组并属于集合容器。这里为了保持内容的连贯性、一致性。故将数组也放在此处一并进行介绍。在Groovy中,数组的表示形式与列表是一致的,均采用方括号。可通过赋值给显式数组变量类型 或 as 等方式创建一个数组,其次Groovy同样支持Java风格的数组创建、初始化方式

示例代码如下所示,可以看到与Java数组最大的区别是支持负数索引下标。其中,-1表示最后一个元素,-2表示倒数第二个元素,依此类推

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
class ArrayDemo {
static void main(args) {
// 通过显式变量类型声明字符串数组
String[] array1 = ['Aaron','Tom']
assert array1.size() == 2
assert array1 instanceof String[]
assert !(array1 instanceof List)

// 通过索引下标访问数组
assert array1[0] == 'Aaron'
// 与List类似,支持负数索引
assert array1[-1] == 'Tom'
// 通过索引下标修改元素
array1[1] = "Tony"
assert array1 == ['Aaron','Tony']

// 通过as运算符强制类型为int数组
def array2 = [123,44,77] as int[]
assert array2 instanceof int[]
assert array2.size() == 3

// 支持Java风格的数组初始化
def array3 = new int[] {110, 119, 120, 114}
assert array3[2] == 120

// 支持Java风格的数组创建
def array4 = new int[4]
array4[2] = 996
assert array4 == [0, 0, 996, 0]
}
}

List 列表

Groovy采用方括号用于表示列表,默认实现类为ArrayList。同样支持负数索引特性。特别地,Groovy还对列表的功能进行了增强,具体地通过重载相关操作符实现。列表的基本操作示例如下所示

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
class ListDemo {   
/**
* List 列表基本操作
*/
static void basic() {
def myList1 = [1,7,2]
// List的默认实现类为ArrayList
assert myList1 instanceof ArrayList
assert myList1.size() == 3

// 将Range转换为List
def myList2 = (0..5).toList()
assert myList2 == [0,1,2,3,4,5]
// 通过索引下标进行访问、修改
assert myList2[3] == 3
myList2[5] = 996
assert myList2[5] == 996
// 支持使用负数索引
// -1表示列表的最后一个元素; -2表示列表倒数第二个元素;以此类推
assert myList2[-1] == 996
myList2[4] = 985
assert myList2[-2] == 985

// 通过Range进行访问
def myList3 = ["Aaron","Bob","C","Tony","Tom","Luca"]
// Range作为索引下标进行访问
assert myList3[0..2] == ["Aaron","Bob","C"]
// 反向Range作为索引下标进行访问, 得到的结果列表也是反向的
assert myList3[2..0] == ["C","Bob","Aaron"]

/********************** 类型指定 **********************/
// 通过显式类型声明
LinkedList myLinkedList1 = [1,3,7]
assert myLinkedList1 instanceof LinkedList

// 直接new一个LinkedList实例
def myLinkedList2 = new LinkedList()
assert myLinkedList2 instanceof LinkedList

// 通过as运算符强制类型
def myLinkedList3 = [] as LinkedList
assert myLinkedList3 instanceof LinkedList

/********************** 重载运算符 **********************/
def myList4 = [996]
// 添加元素到列表
myList4 += "Aaron"
// 列表元素支持异构类型
assert myList4 == [996, "Aaron"]
// 添加集合到列表
myList4 += [true, false]
assert myList4 == [996, "Aaron", true, false]
// 从列表中移除元素
myList4 -= "Aaron"
// 从列表中移除集合
myList4 -= [true, 996]
assert myList4 == [false]
// 向元素尾部追加元素
myList4 << "Bob" << 17
assert myList4 == [false, "Bob", 17]
}
}

Groovy对列表的操作进行增强,使得我们可以很方便将其作为一个stack栈进行操作。示例代码如下所示

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
class ListDemo {   
/**
* 像栈Stack一样操作
*/
static void likeStack() {
def list1 = [985, "Tina"]
// 入栈: 从列表的头部添加元素
list1.push("Aaron")
assert list1 == ["Aaron", 985, "Tina"]
// 出栈: 从列表的头部移除元素
def e = list1.pop()
assert e == "Aaron"
assert list1 == [985, "Tina"]
}
}

为了更好的使用列表,Groovy对Java列表中原有的方法进行了拓展、增强。具体如下所示

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
class ListDemo {   
/**
* 列表其它常用操作
*/
static void other() {
def list1 = [1,2,3,7,4]
// Groovy的inject方法类似于Java Stream的reduce方法
// 这里参数0作为闭包中第一个参数acc的初值
def sum1 = list1.inject(0) {acc, e -> acc+e}
// sum1 = 1+2+3+7+4 = 17
assert sum1 == 17
def maxNum = list1.inject(Integer.MIN_VALUE) { result, e -> Integer.max(result,e) }
assert maxNum == 7

def list2 = list1.collect {e -> e*2 }
assert list1 == [1,2,3,7,4]
assert list2 == [2,4,6,14,8]

// 分别获取奇数、偶数
def oddList = list1.findAll {e -> e%2==1 }
def evenList = list1.findAll {e -> e%2==0 }
assert oddList == [1,3,7]
assert evenList == [2,4]
// 查找第一个满足闭包条件的元素
def num = list1.find {e -> e>3}
assert num == 7
// 列表中每一个元素是否均满足闭包条件
def isAllPositive = list1.every {e -> e>0 }
assert isAllPositive
def result = list1.every {e -> e<3 }
assert !result
// 列表中是否存在一个元素满足闭包条件
def result2 = list1.any {e -> e<3 }
assert result2

def list3 = [1,22,13,24,15,15,3,9,22]
// 对22进行计数
def count1 = list3.count(22)
assert count1 == 2
// 列表的最大值
def max = list3.max()
assert max == 24
// 列表的最小值
def min = list3.min()
assert min == 1

// 通过Set实现列表去重
def list4 = new HashSet(list3).toList()
assert list3 == [1,22,13,24,15,15,3,9,22]
assert list4 == [1, 3, 22, 24, 9, 13, 15]
// 去重同时保留顺序
def list5 = list3.unique()
assert list5 == [1, 22, 13, 24, 15, 3, 9]

// 正向遍历
def list6 = ["Aaron","Tina","Bob"]
def str1 = ""
list6.each {e -> str1 += e + "~" }
assert str1 == "Aaron~Tina~Bob~"
// 反向遍历
def str2 = ""
list6.reverseEach {e -> str2 += e+"~" }
assert str2 == "Bob~Tina~Aaron~"
// 更优雅地拼接
def str3 = list6.join("~")
assert str3 == "Aaron~Tina~Bob"
}
}

前面我们提到Switch语句支持分类器,其本质上就是通过调用isCase方法判断是否属于某分类。事实上,对于Groovy中的集合类均存在一个Grep过滤器方法,用于过滤、筛选出符合要求的元素。这里其实就是隐式调用了过滤器的isCase方法。这里以List集合为例,展示如何使用Grep方法

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
class ListDemo {   
/**
* Grep过滤器
* @apinote 集合类均支持grep过滤器
*/
static void testGrep() {
def list1 = [1,5,"Aaron",4,"Bob",25]

// 调用Integer的isCase方法
def list2 = list1.grep( Integer )
assert list2 == [1,5,4,25]

// 调用String的isCase方法
def list3 = list1.grep( String )
assert list3 == ["Aaron", "Bob"]

// 调用Range的isCase方法
def list4 = list1.grep( 0..<5 )
assert list4 == [1,4]

def closure1 = { e ->
if( e instanceof Integer && e%5==0) {
return true
}
return false
}
// 调用闭包的isCase方法
def list5 = list1.grep( closure1 )
assert list5 == [5,25]
}
}

Map 映射

对于Map映射而言,Groovy采用[:]语法进行表示。特别的是,Groovy支持将key作为下标获取Value。基本操作方式示例如下所示

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
class MapDemo {
static void basic() {
def map1 = ["Aaron":25, "Bob":18, "Tony": 35]
// Map的默认实现类为LinkedHashMap
assert map1 instanceof LinkedHashMap
assert map1.size() == 3
// 通过Key作为下标访问Value
assert map1["Bob"] == 18
// 通过get方法访问Value
assert map1.get("Bob") == 18
assert map1.get("Tom", 996) == 996
// 通过点操作符访问Value
assert map1."Bob" == 18
assert map1.Bob == 18

// 修改、添加操作类似
map1["Aaron"] = 2
map1.put("Bob", 1)
map1."Tim" = 7
assert map1 == ["Aaron":2, "Bob":1, "Tony":35, "Tim":7, "Tom":996]

// 迭代Map中的条目按闭包进行计算,并将结果保存到列表中
def list1 = map1.collect{ entry -> entry.value+1000 }
assert list1 instanceof List
assert list1 == [1002, 1001, 1035, 1996, 1007]

def list2 = []
// 将结果保存到给定的List中
map1.collect(list2) { entry -> entry.value+2000}
assert list2 instanceof List
assert list2 == [2002, 2001, 2035, 2996, 2007]

// Map中每一个KV对是否均满足闭包条件
assert map1.every {entry -> entry.value>0}
assert !map1.every {entry -> entry.value>3}
// Map中是否存在一个KV对满足闭包条件
assert map1.any {entry -> entry.key=="Aaron"}

def map5 = ["Bob":3, "Aaron":18, "Tom": 23]
// 查找任一一个满足闭包条件的条目
def entry1 = map5.find { entry -> entry.value>5 }
assert entry1.toString() == "Aaron=18"

// 查找所有满足闭包条件的条目
def resutMap = map5.findAll { entry -> entry.value>5 }
assert resutMap == [Aaron:18, Tom:23]
}
}

在Groovy中,Map的默认实现类为LinkedHashMap。如果期望选择别的实现类也是可以的。示例如下所示

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
class MapDemo {
static void testType() {
/********************** 类型指定 **********************/
// 定义一个空Map, 通过as运算符强制类型为TreeMap
def map2 = [:] as TreeMap
assert map2 instanceof TreeMap

// 直接new一个TreeMap实例
def map3 = new TreeMap()
assert map3 instanceof TreeMap

// 通过显式类型声明
TreeMap map4 = ["Aaron":25, "Bob":18, "Tony": 35]
assert map4 instanceof TreeMap
}
}

Range 范围

特别地,Groovy中还提供了Range范围这一集合类型。大大简化了Java中依靠传统for循环不停递增变量的编程方式。示例代码如下所示

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
class RangeDemo {
static void main(args) {
// range1: [0, 5]
def range1 = 0..5
assert range1 instanceof Range
// Range实现了List接口
assert range1 instanceof List
// contains: 检测某个元素是否存在于范围中
assert range1.contains(0)
assert range1.contains(3)
assert range1.contains(5)
assert range1.size() == 6

def sum = 0
// each: 对范围中每个元素执行闭包
range1.each { e -> sum += e }
// sum = 0+1+2+3+4+5 = 15
assert sum == 15

// range2: [0, 5)
def range2 = 0..<5
assert range2.contains(5) == false

// 日期范围
def today = new Date()
def yesterday = today - 1
assert (yesterday..today).size() == 2

// 字符范围
def range3 = 'a'..'c'
assert range3.contains("b")

// 反向范围
def range4 = 4..<1
assert range4.toList() == [4, 3, 2]
}
}

参考文献

  1. Groovy In Action · 2nd Edition Dierk König、Guillaume Laforge著
请我喝杯咖啡捏~

欢迎关注我的微信公众号:青灯抽丝