Groovy核心一字符串集合运算符重载

52次阅读

共计 11086 个字符,预计需要花费 28 分钟才能阅读完成。

Groovy 是增强 Java 平台的唯一的脚本语言;与 Java 相比,脚本语言具有更好的表示能力,能够提供更高的抽象等级。
Groovy 语言特点:

  1. Groovy 是一种面向对象的脚本语言,涉及的所有事物都是对象,这一点不像 Java 语言
  2. Groovy 与 Java 是二进制兼容的,Groovy 编译器产生的字节码与 Java 编译器产生的字节码是完全一样的
  3. Groovy 能够完全使用各种 Java API
  4. Groovy 是动态类型语言,类型标记存在于对象中,而不是由引用它的变量来决定,所以 Groovy 不要求声明变量的类型、方法的参数或者方法的返回值
  5. Groovy 语言内置数据结构,如:List 对象或者 Map 对象

学习这门语言,主要是为了看懂这门脚本语言,知道在 Gradle 中为什么这么写。我们以后在 Gradle 中使用 Groovy 脚本文件,既可以写任何符合 Groovy 语法的代码,而 Groovy 又完全兼容 java,意味着可以在 build 脚本文件里写任何的 java 代码,是相当的灵活。

基础语法讲解

在 Groovy 中每一个事物最终会被当作某些类的一个实例对象。例如:Groovy 中,整数 123 实际上是 Integer 类的一个实例。Groovy 同样支持运算符重载,也就是 +号可以出现在 两个参数之间,例如:123+456,但是可以通过对象调用的方式,123.plus(456)

Groovy 支持整数和浮点数。注意:整数都是 Integer 类的实例,浮点数是 BigDecimal 类的实例。

def b = 3.14
println b.getClass()

// 打印结果
class java.math.BigDecimal

注意:Groovy 中使用关键字def 来声明变量。

关系运算符(<、>、>=、<=)和等于运算符(==、!=)最终都是调用某个方法实现的。关系运算符通过调用 compareTo 方法实现的,等于运算符通过调用 equals 方法实现的。

// 3 > 5
3.compareTo(5) > 0

def age = 23
def number = 24
// age.equals(number)是通过 Integer 类的 equals 方法实现
age == number 

字符串讲解

在 Groovy 中,语句后面分号不是必需的,这是 Groovy 的特性。

在 Groovy 中,可以使用单引号 (’)、双引号(”) 或者三引号 (”’) 来封装字符串。单引号表示纯粹的字符串常量,而不能对字符串里的表达式做运算,但是双引号可以。不管哪种方式表示字符串,它们都是 String 类型。

def age = 23;
println 'my age is ${age}'
println "my age is ${age}"
println '''my age is ${age}'''
println "my age is \${age}"

// 打印结果
my age is ${age}
my age is 23
my age is ${age}
my age is ${age}

从上面的示例可以看出:单引号中 ${age} 没有被解释;如果在美元符号($)前使用了反斜杠()转义字符后,也不会被解释。也就是说,只在字符串需要被解释的时候使用双引号,在其他情况下字符串使用单引号,单引号没有运算能力

我们可以通过索引(index)来指定字符串中单个字符或者指定字符集合。一般索引从零开始,该字符串的长度值减一结束。如果负数索引,从字符串的末尾开始往前计算。

def string = "Hello Groovy"
println string[1]  // e
println string[-1]  // y
println string[1..2]  // el    [1..2]表示 1 到 2 的范围
println string[1..<3]  // el
println string[4, 2, 1] // ole

字符串基本操作

def string = "Hello Groovy"
println "Hello" + "Groovy" // Hello Groovy 字符串的合并,相当于 "Hello".plus("Groovy")
println "Hello" * 3   // HelloHelloHello 字符串的赋值
println string - "o Groovy" // Hell 删除首次出现的字符串
println string.length()   // 12 获取字符串的长度
println string.size()     // 12 获取字符串的长度
println string.contains("Groovy")  // true 字符串中是否包含指定的子字符串
println string.count("o")   // 3 统计某个子字符串出现的次数

字符串方法
Groovy 开发工具(GDK)扩展了 String 类的一些方法,如:

方法名称 函数原型
center String center(Number numberOfChars, CharSequence padding); 返回左右均使用 padding 填充的新字符串,默认使用空格填充
eachMatch String eachMatch(String regex,Closure closure); 判断该字符串是否与正则表达式 regex 匹配。传递给闭包的对象是一个匹配成功的字符串数组
leftShift StringBuffer leftShift(Object value); 可以将多个字符串对象相加,并返回新的字符串。与 StringBuffer 功能一样
next String next(); 字符串的末尾字符替换为末尾字符 + 1 后的字符,并将字符串返回
getAt String getAt(int index); String getAt(IntRange range); String getAt(Collection indices); 根据传入的索引或者范围获取对应的字符
padLeft String padLeft(Number numberOfChars, CharSequence padding); 在字符串的左边使用 padding 填充,默认空格填充
padRight String padRight(Number numberOfChars, CharSequence padding); 在字符串的右边使用 padding 填充,默认空格填充
plus String plus(CharSequence value); 字符串相加
previous String previous(); 字符串的末尾字符替换为末尾字符 - 1 后的字符,并将字符串返回
reverse String reverse(); 创建当前字符串的逆序字符串
size int size() 返回字符串的长度
split String[] split(String regex); 与给定正则表达式匹配的规则切割字符串
tokenize List<String> tokenize(CharSequence delimiters); 使用 delimiters 作为字符串的分隔符,默认使用空格作为分隔符

下面示例演示:

def string = "Groovy"
println string.size()  // 6

println string.center(4) // Groovy
println string.center(11, '#')  // ##Groovy###

println string.eachMatch("oo") {ch -> print ch  // oo} // Groovy

println string.getAt(0) // G
println string.getAt(1..3) // roo
println string.getAt([0, 2, 4]) // Gov

println string.leftShift("##") // Groovy##

println string.next() // Groovz
println string.previous() // Groovx

println string.padLeft(8, "#") // ##Groovy
println string.padRight(9, "#") // Groovy###

println string.plus(".") // Groovy.

println string.reverse() // yvoorG

println string.split('oo') // [Gr, vy]

println string.tokenize('r') // [G, oovy]

字符串比较
Groovy 中,字符串比较采用词典顺序,大写字母排在小写字母前面。如果 str1 与 str2 比较,str1 和 str2 相同,返回 0;如果 str1 在 str2 前面,返回 -1;如果 str1 在 str2 后面,返回 1

正则表达式
正则表达式是在文本中寻找子字符串的一种模式。
在正则表达式中有两个特殊的位置标识符,脱字符号 ^ 和美元符号 $, 分别表示某行的开始与结尾。
正则表达式也包含数量。加符号 + 表示出现一次或者多次,星号* 表示出现零次或者多次,?表示出现一次或者一次也没有,{} 用来匹配位于 { 符号之前指定次数的字符,如:X{n} X 恰好出现 n 次,X{n,} X 至少出现 n 次,X{n,m} X 至少出现 n 次,但是不超过 m 次

println "aaaaaaab".matches('a*b') // true
println "b".matches('a*b') // true
println "aaaab".matches('a*c?b') // true
println "aaaaab".matches('a{5}b') // true
println "aaab".matches('a{3,}b') // true

在正则表达式中,点符号 . 表示任意字符,也就是通配符。注意:在正则表达式中需要使用 \\\\ 四个反斜杠表示一个反斜杠。

println '3.14'.matches('3.14') // true
println '3C14'.matches('3.14') // true
println '3.14'.matches('3\\.14') // true
println '3C14'.matches('3\\.14') // false

正则表达式还可以表示字符类。通过 []元字符表示。如:
[abc] 表示 a、b 或 c .
[^abc]任何字符,除了 a、b 或 c(否定)
[a-zA-Z] a 到 z 或 A 到 Z,两头的字母包括在内(范围)
\d 数字:[0-9]
\D 非数字:1
\w 单词字符:[a-zA-Z_0-9]
\W 非单词字符:2
XY X 后跟 Y
X|Y X 或 Y
(X) X,作为捕获组

println 'ababab'.matches('(ab)*') // true
println 'abababa'.matches('(ab)*') // false
println 'abababa'.matches('(ab)*a') // true
println 'aaac'.matches('([ab])*c') // true
println 'aaac'.matches('(a|b)*c') // true
println 'ababc'.matches('([ab])*c') // true

在 Groovy 正则表达式中也支持 / 分隔符模式,所以不用重复所有的反斜杠符号。

println "\$abc.".matches('\\\$(.*)\\.') // true
// 也可以下面这张写法
println "\$abc.".matches(/\$(.*)\./)  // true

列表(List)、映射(Map)、范围(Range)

这三种数据结构都是引用其他对象的集合。List 和 Map 能够引用不同类型的对象,Range 表示整数值的集合。List 中每个对象通过一个整数值索引 index 获取,Map 能够通过任意类型的值来索引。Groovy 完全兼容了 java 的集合,并且进行了扩展。

列表(List)

Groovy 列表定义方式,下面例子可以看出:Groovy 列表类型使用的就是 Java 中的 ArrayList

// 可以在 Groovy 中直接创建 ArrayList 方式,但是太繁琐
def list = new ArrayList()
println list.getClass() // class java.util.ArrayList

// Groovy 定义方式,是不是很简单啊
def list2 = [11, 12, 13, 14]
println list2.class // class java.util.ArrayList

// 可以嵌套不同类型元素
def list3 = def list3 = [12, 23, "kerwin", [32, 43]]

Groovy 列表通过 索引操作符 [index] 来获取列表中的元素值,列表的索引 index 从 0 开始,指向列表的第一个元素值。索引操作符其实就是 List 类中定义的 getAt 方法。负索引表示从右边开始,- 1 就是代表从右侧数第一个。

def list = [11, 12, 13, 14]
println list[0]  // 访问第 0 个元素的值为 11,  list.getAt(0) 
println list[3]  // 访问第 3 个元素的值为 14,  list.getAt(3) 
println list[-1]  // 访问最后一个元素 14,  list.getAt(-1)

通过索引 index 范围操作列表,包含边界的索引范围如:start..end,不包含 end 边界的索引范围如start..<end

def list = [11, 12, 13, 14]
println list[0..2] // [11, 12, 13]
println list[1..<3] // [12, 13]

同样列表的索引操作符也可以为 List 赋值,也可以通过 putAt 方法实现赋值操作.

def list = [11, 12, 13, 14]
list[1] = 22
println list.toListString() // [11, 22, 13, 14]
列表(List)操作

在 Groovy 中,可以通过 List 类提供的方法操作。但是 Groovy 提供了非常简单的方法。如集合的迭代操作 each 方法,该方法接受一个闭包作为参数,可以访问 List 中的每个元素

def list = [11, 12, 13, 14]
list.each {println "${it}"
}

向列表中添加元素
可以通过 << 运算符(即 leftShift)将一个新元素值追加到列表的最后;
同样可以使用 + 操作符(即 plus)追加元素或者连接两个列表,注意:plus(Collection<T> right) 方法返回的 List 是一个新的列表,元素由原始列表中的元素和 right 中元素组成.

def list = [11, 12, 13, 14]
list.add(15) // [11, 12, 13, 14, 15]
list.leftShift(16) // [11, 12, 13, 14, 15, 16]
list << 17 // [11, 12, 13, 14, 15, 16, 17]
println list.toListString()

// 注意:plus 方法返回是一个新的 List 列表
def plusList = list.plus([18, 19]) // [11, 12, 13, 14, 15, 16, 17, 18, 19]
println plusList.toListString()

def plusList2 = plusList + [20, 21] // [11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21]
println plusList2.toListString()

从列表中删除元素
调用 remove 方法删除元素的时候,我们需要注意 remove(int index)remove(Object o)的区别,第一个是根据索引 index 删除列表中的元素,而第二个是根据列表中的元素删除,所以我们可以调用 removeAt(int index)removeElement(Object o)来区别

def list = [11, 12, 13, 14]
list.remove(2); // [11, 12, 14]
// 需要注意点
//list.remove(14) // Caught: java.lang.IndexOutOfBoundsException: Index: 14, Size: 3
list.remove((Object)14) // [11, 12]

list.removeAt(1); // [11]
list.removeElement(11) // []

我们可以调用 removeAll(Closure condition) 同时删除列表中多个元素,参数是闭包, 闭包是 boolean 类型返回值

def list2 = [21, 22, 23, 24]
list2.removeAll {return it % 2 == 0} //

还可以使用 - 操作符(即 minus 方法)从列表中删除元素; 注意:minus(Collection<?> removeMe)方法返回的 List 是一个新列表,新列表中的元素由原始列表中的元素移除 removeMe 中元素组成。

def list3 = [31, 32, 33, 34]
def minusList = list3.minus(32) // [31, 33, 34]
def minusList2 = list3.minus([32, 33]) // [31, 34]
def minusList3 = list3 - [33, 34] // [31, 32]

列表元素排序
在 Groovy 中排序 sort(boolean mutate,Closure closure) 方法有多个重载方法,传入的闭包方式需要有比较的参数并返回 0、-1、或者 1

def list = [12, -3, 2, 8, -1, 34]

// 第一种方式
Comparator mc = {a, b -> a == b ? 0 : Math.abs(a) < Math.abs(b) ? 1 : -1
}
Collections.sort(list, mc) // [34, 12, 8, -3, 2, -1]

// 第二种方式
list.sort {a, b -> a == b ? 0 : Math.abs(a) < Math.abs(b) ? -1 : 1
} // [-1, 2, -3, 8, 12, 34]

// boolean mutate 该参数表示是否创建新的列表存放排序后的元素
// false: 创建新的列表存放
def sortList = list.sort(false) {a, b -> a == b ? 0 : Math.abs(a) < Math.abs(b) ? 1 : -1
} // [34, 12, 8, -3, 2, -1]

列表中查找元素

def list = [-3, 9, 6, 2, -7, 1, 5]
// 返回查找第一个满足条件的元素,没有找到返回 null
def result = list.find {return it % 2 == 0} // 6

// 返回查找所有满足条件的元素,没有找到返回空的列表
def resultAll = list.findAll {return it % 2 == 0} // [6, 2]

// 列表中只要一个元素满足条件就返回 true,否则返回 false
def resultAny = list.any {return it % 2 == 0} // true

// 列表中所有的元素都满足条件返回 true,否则返回 false
def resultEvery = list.every {return it % 2 == 0} // false

// 查找列表中最大的值
println list.max {return Math.abs(it)
} // 9

// 查找列表最小的值
println list.min() // -7

// 统计列表中满足条件的元素总个数
println list.count {return it % 2 == 0} // 2

映射(Map)

Map 用法与 List 很像。Groovy 映射定义方式,从下面例子可以看出:Groovy 列表使用的就是 Java 中的 LinkedHashMap

def colors = [red  : 'ff0000',
              green: '00ff00',
              blue : '0000ff']
println colors.getClass() // java.util.LinkedHashMap
println colors.toMapString() // [red:ff0000, green:00ff00, blue:0000ff]
// 注意:// 空 Map 定义方式
def nullMap = [:]
println nullMap.toMapString() // [:]

映射赋值及访问可以通过索引的方式,和列表一样,也可以通过 getAt 方法访问,通过 putAt 方法赋值。从下面示例可知,Map 访问通过 map.key 或者 map[key] 都可以。

def colors = [red  : 'ff0000',
              green: '00ff00',
              blue : '0000ff']
// 访问
println colors['red'] // ff0000
println colors.blue // 0000ff
println colors['yellow'] // null
println colors.getAt('green') // 00ff00

// 赋值
colors.yellow = 'ffff00'
// 或者  colors['yellow'] = 'ffff00'
// 或者  colors.putAt('yellow', 'ffff00')
println colors.toMapString() // [red:ff0000, green:00ff00, blue:0000ff, yellow:ffff00]

映射(Map)操作

为了方便操作 Map,先定义 students 集合数据。

def students = [
        1: [number: '0001', name: 'Bob',
            score : 55, sex: 'male'],
        2: [number: '0002', name: 'Johnny',
            score : 62, sex: 'female'],
        3: [number: '0003', name: 'Claire',
            score : 73, sex: 'female'],
        4: [number: '0004', name: 'Amy',
            score : 66, sex: 'male']
]

Map 遍历,GDK 提供了很多 each 方法。可以通过 Map.Entry、索引 index 或者 key-value 方式

// 如果闭包传入一个参数,是通过 Map.Entry 遍历
students.each {def student -> println "the kay is ${student.key}, the value is ${student.value}"
}

// 如果闭包传入 2 个参数,是通过 key-value 方式遍历
students.each {key, value -> println "the kay is ${key}, the value is ${value}"
}

// 遍历的时候,获取 index
students.eachWithIndex{def student, int index -> println "index is ${index}, the kay is ${student.key}, the value is ${student.value}"
}
// 遍历的时候,获取 index
students.eachWithIndex{key, value, index -> println "index is ${index}, the kay is ${key}, the value is ${value}"
}

Map 查找

// 在 Map 中查找第一个满足条件,找到返回的是 Entry 对象,没有找到返回 null
def result = students.find {def student -> return student.value.score >= 60}
println result // 2={number=0002, name=Johnny, score=62, sex=female}

// 在 Map 中查找所有满足条件的,并将满足条件的数据存放在新的 Map 中返回
def resultAll = students.findAll {key, value -> value.score >= 60}
//[2:[number:0002, name:Johnny, score:62, sex:female],
// 3:[number:0003, name:Claire, score:73, sex:female],
// 4:[number:0004, name:Amy, score:66, sex:male]]
println resultAll.toMapString()

// 查找所有及格的学生姓名
def resultList = students.findAll {def student -> student.value.score >= 60}.collect {key, value -> value.name}
println resultList.toListString() // [Johnny, Claire, Amy]

Map 分组

// 按成绩及格与不及格分组
def resultGroupMap = students.groupBy {def student -> return student.value.score >= 60 ? "及格" : "不及格"}
// [不及格:[1:[number:0001, name:Bob, score:55, sex:male]],
//  及格:[2:[number:0002, name:Johnny, score:62, sex:female],
//          3:[number:0003, name:Claire, score:73, sex:female],
//          4:[number:0004, name:Amy, score:66, sex:male]]]
println resultGroupMap.toMapString()

Map 排序

// 排序
def resultSortMap = students.sort {
    def student1, def student2 ->
        Number score1 = student1.value.score as Number
        Number score2 = student2.value.score as Number
        return score1 == score2 ? 0 : score1 < score2 ? -1 : 1
}
//[1:[number:0001, name:Bob, score:55, sex:male],
// 2:[number:0002, name:Johnny, score:62, sex:female],
// 4:[number:0004, name:Amy, score:66, sex:male],
// 3:[number:0003, name:Claire, score:73, sex:female]]
println resultSortMap.toMapString()

范围(Range)

范围包含边界的范围通过 .. 表示,不包含边界的范围则在下边界和上边界之间使用..<。范围可以通过字符串或者整型值表示.

def range = 1..10
// 获取当前范围中元素个数
println range.size() // 10
// 通过索引获取范围中元素
println range[2] // 3

// 可以通过 from、to 获取当前范围的最小元素值、最大元素值
println range.from // 1
println range.to // 10

// 判断范围中是否包含给定的元素
println range.contains(4)

// 通过字符串表示范围
def start = 1
def finish = 4
def rangeString = start..finish
println rangeString.getClass() // class groovy.lang.IntRange
println rangeString.toListString() // [1, 2, 3, 4]

  1. 0-9 ↩
  2. w ↩

正文完
 0