【Gradle】Groovy筑基(二)数据结构与文件操作

1,537

一、常用的数据结构

1. 1 List

定义一个List的方式与我们平时定义的一样,有两种方式,一种是def list = new ArrayList(),另一种是def list = [1, 2, 3, 4, 5],这样定义出来的list默认也是ArrayList;而定义一个数组则是def array = [1, 2, 3, 4, 5] as int[],如果要使用Java的语法来创建数组也是一样的。

  • 添加
def list = [1, 2, 3, 4, 5]
// 第一种方式:在最后添加一个元素6
list.add(6)
println list

// 第二种方式:在下标为3的位置添加元素9
list.add(3, 9)
println list

// 第三种方式(这种方式在Java中是没有的):在最后添加元素11
list<<11
println list

// 第四种方式(只针对于新对象):创建newList,并在list的基础上添加多一个10
def newList = list + 10
println newList

打印结果如下:

image.png

  • 删除
def list = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
// 第一种方式:删除下标2的元素
list.remove(2)
println list

// 第二种方式:删除元素4
list.remove((Object) 4)
println list

// 第三种方式:删除元素5
list.removeElement(5)
println list

// 第四种方式:闭包,删除符合条件的所有
list.removeAll {
    return it % 5 != 0
}
println list

打印的结果分别为: image.png

  • 查找
def list = [5, -2, 1, 4, 3]
// 查找满足条件的第一个数据
println list.find {
    it % 2 == 0
}

// 查找所有满足条件的数据
println list.findAll {
    it % 2 != 0
}

// 查找是否有满足条件的数据
println list.any {
    it % 2 == 0
}

// 查找是否全部满足条件
println list.every {
    it % 2 == 0
}

// 统计功能
println list.count {
    it > 0
}

打印的结果分别为:

image.png

  • 排序
def sortList = [5, -2, 1, 4, 3]
// 默认的排序规则是按照从小到大排序的
println sortList.sort()

// 按绝对值大小排序
println sortList.sort { a, b ->
    a == b ? 0 : Math.abs(a) > Math.abs(b) ? 1 : -1
}

// 对象排序
def sortStringList = ['aaaaa', 'bbbb', 'c', 'ddd', 'ee']
// 按照String的size大小进行排序
println sortStringList.sort {
    it.size()
}

打印结果如下:

image.png

1. 2 Map

首先我们来看一下Map的如何定义以及读取的

// Map的定义与读取
def colors = [red: 'ff0000', green: '00ff00', blue: '0000ff']
println colors['green']
println colors.green

// 看一下默认使用的数据结构是什么
println colors.getClass()

可以看到,我们在定义一个Map的时候,对于key,我们是可以不写引号的,当然如果要培养良好的习惯的话,也可以写上引号。而在读取的时候也一样,有带引号跟不带引号两种方式可以读取。

输出结果:

image.png

可以看到,以这种方式定义的Map默认使用的数据结构是LinkedHashMap。当然,熟悉Java的也可以使用Java的方式来定义一个Map,这里不做赘述。

  • 添加
def colors = ['red': 'ff0000', 'green': '00ff00', blue: '0000ff']

// 添加普通对象
colors.yellow = 'ffff00'
println colors

// 添加集合对象
colors.map = [key1: 1, key2: 2]
println colors

输出结果:

image.png

  • 遍历
def students = [
        1: [number: '001', name: 'youzi01'],
        4: [number: '004', name: 'youzi04'],
        3: [number: '003', name: 'youzi03'],
        2: [number: '002', name: 'youzi02'],
        6: [number: '006', name: 'youzi06'],
        5: [number: '005', name: 'youzi05'],
]

// 用键值对的方式遍历
students.each { def key, def value ->
    println "key = $key, value = $value"
}

// 用entry对象的方式遍历
students.each { student ->
    println "key = ${student.key}, value = ${student.value}"
}

// 使用带索引的方式进行遍历
students.eachWithIndex { def student, int index ->
    println "index = $index, key = ${student.key}, value = ${student.value}"
}

这里重新定义了一个Map对象,然后通过三种常见的遍历方式来测试。这里不作输出结果的展示。

  • 查找
def students = [
        1: [number: '001', name: 'youzi01'],
        4: [number: '004', name: 'youzi04'],
        3: [number: '003', name: 'youzi03'],
        2: [number: '002', name: 'youzi02'],
        6: [number: '006', name: 'youzi06'],
        5: [number: '005', name: 'youzi02'],
]

// 查找第一个符合条件的数据
println students.find { student ->
    student.value.name == 'youzi02'
}

// 查找符合条件的所有数据
println students.findAll { student ->
    student.value.name == 'youzi02'
}

// 统计功能
println students.count { student ->
    student.value.name == 'youzi02'
}

// 实现嵌套查询
println students.findAll { student ->
    student.value.name == 'youzi02'
}.collect {
    it.value.number
}

这里的查找其实与上面的List差不多,所以如果懂了List的话,Map也是很容易上手的。

输出结果如下:

image.png

  • 排序 这里不对排序做示例讲解,与上面的List一样。唯一要注意的是:Map在排序的时候会返回一个新的Map,而List是在原来的List上做排序。

1. 3 Range

Range的使用是比较简单的,下面先来看一下如何定义以及简单使用。

// 定义一个range
def range = 1..10

// 获取range的0下标
println "range的0下标:${range[0]}"

// 判断range中是否包含8
println "range中是否包含8:${range.contains(8)}"

// 获取range中的起点
println "range的起点:${range.from}"

// 获取range中的终点
println "range的终点:${range.to}"

输出结果:

image.png

我们可以点进Range的源码里查看,会发现其实Range是继承自List的

image.png

所以Range的增删查排序跟List是完全一样的。

二、序列化与文件操作

2. 1 json解析

  • 对象转json字符串 首先定义一个简单的Person类
class Person {
    String name
    int age
}

然后创建一个lists,并把这个lists转换成json格式的字符串并输出。

def lists = [new Person(name: "Sherry", age: 18), new Person(name: "Miya", age: 1)]

// 第一种方式:把list转换成json格式并输出
println "第一种方式输出:"
println JsonOutput.toJson(list)

// 第二种方式:把list转换成json字符串并且格式化后再输出
println "第二种方式输出:"
println JsonOutput.prettyPrint(JsonOutput.toJson(list))

输出结果:

第一种方式输出:
[{"age":18,"name":"Sherry"},{"age":1,"name":"Miya"}]

第二种方式输出:
[    {        "age": 18,        "name": "Sherry"    },    {        "age": 1,        "name": "Miya"    }]

可根据上面的两种输出方式来查看对应的结果。

  • json字符串转对象 接下来来看一下json字符串如何转换成对象,我们通过上面打印出的json字符串来进行测试:
def jsonSlurper = new JsonSlurper()
println jsonSlurper.parse("[{\"age\":18,\"name\":\"Sherry\"},{\"age\":1,\"name\":\"Miya\"}]".getBytes())

这个parse可以接收的数据类型是很多的,这里使用bytes来进行测试,有兴趣的可以自己看一下都支持哪些数据类型。现在来看一下输出结果:

[[age:18, name:Sherry], [age:1, name:Miya]]

可以看到输出结果对应上了上面定义的lists,可以说Groovy提供的这些转换的api很方便了。

2. 2 xml解析

  • 解析xml 在Groovy中解析xml也是很简单的,下面来看个例子:
// 首先使用三引号来定义一段xml格式的文件,这样保证了该文件是以原始的格式输出的
final String xml='''
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    package="com.imiyar.groovydemo">
    <test>12345</test>
    <application
        android:allowBackup="true"
        android:icon="@mipmap/ic_launcher"
        android:label="@string/app_name"
        android:roundIcon="@mipmap/ic_launcher_round"
        android:supportsRtl="true"
        android:theme="@style/AppTheme">
        <activity android:name=".SecondActivity"/>
        <activity android:name=".MainActivity">
            <intent-filter>
                <action android:name="android.intent.action.MAIN" />
                <category android:name="android.intent.category.LAUNCHER" />
            </intent-filter>
        </activity>
    </application>
</manifest>
'''

// 然后来定义Xml的解析对象
def xmlSlurper = new XmlSlurper()
// 这里的result拿到的只是根节点,如果要获取子节点,需要通过根节点去获取
def root = xmlSlurper.parseText(xml)
// 读取根节点的package属性
println root.@package
// 读取非命名空间的test节点
println root.test.text()
// 读取命名空间的节点
root.declareNamespace('android': 'http://schemas.android.com/apk/res/android')
// 这里需要注意的是,如果是读取命名空间的属性的话需要加单引号
println root.application.@'android:theme'
// 遍历activity节点并输出名字
root.application.activity.each { activity ->
    println activity.@'android:name'
}

上面的注释已经写得很清楚了,唯一要注意的就是有些属性是需要通过declareNamespace()先读取到命名空间,然后才可以读取得到该命名空间下的属性

输出结果:

com.imiyar.groovydemo
12345
@style/AppTheme
.SecondActivity
.MainActivity
  • 生成xml文件 假设我们要生成的xml文件是下面这样的:
<html>
	<title id='123' name='android'>
		<value>xml生成的</value>
	</title>
	<body name='java'>
		<activity id='001' class='MainActivity'>abc</activity>
		<activity id='002' class='SecActivity'>abc</activity>
	</body>
</html>

直接上代码:

def sw = new StringWriter()
def xmlBuilder = new MarkupBuilder(sw)

xmlBuilder.html() {
    title(id: '123', name: 'android') {
        value('xml生成的')
    }
    body(name: 'java') {
        activity(id: '001', class: 'MainActivity','abc')
        activity(id: '002', class: 'SecActivity','abc')
    }
}
println sw

总的来说,就是根据你要生成xml格式,然后分析层级关系,像上面的xml文件中,html是根节点,title跟body是html的子节点,并且title跟body是同级关系,所以需要并列写。

输出结果的结果就如上面的示例结果一模一样啦。

2. 3 文件操作

  • 读文件 Groovy对于读取文件的操作还是很简单的,这里使用几个简单的例子来演示如何读取一个文件。
def file = new File("/Users/sherry/IdeaProjects/HelloWorldGroovy/HelloWorldGroovy.iml")
// 第一种读取:遍历文件
file.eachLine { line ->
    println line
}

// 第二种读取:返回所有文本
println file.getText()

// 第三种读取:以List<String>返回文件的每一行
def text = file.readLines()
println text.toListString()

// 第四种读取:以java中流的方式读取文件内容
def reader = file.withReader { reader ->
    // 读取文件的前100个字符
    char[] buffer = new char[100]
    reader.read(buffer)
    return buffer
}
println reader

这里我使用的是创建Groovy项目时自动生成的一个.iml文件,现在来看一下输出结果:

第一种读取跟第二种读取方式返回的结果一样:

<?xml version="1.0" encoding="UTF-8"?>
<module type="JAVA_MODULE" version="4">
  <component name="NewModuleRootManager" inherit-compiler-output="true">
    <exclude-output />
    <content url="file://$MODULE_DIR$">
      <sourceFolder url="file://$MODULE_DIR$/src" isTestSource="false" />
    </content>
    <orderEntry type="inheritedJdk" />
    <orderEntry type="sourceFolder" forTests="false" />
    <orderEntry type="library" name="groovy-3.0.9" level="application" />
  </component>
</module>

第三种读取结果(以List的形式输出):

[<?xml version="1.0" encoding="UTF-8"?>, <module type="JAVA_MODULE" version="4">,   <component name="NewModuleRootManager" inherit-compiler-output="true">,     <exclude-output />,     <content url="file://$MODULE_DIR$">,       <sourceFolder url="file://$MODULE_DIR$/src" isTestSource="false" />,     </content>,     <orderEntry type="inheritedJdk" />,     <orderEntry type="sourceFolder" forTests="false" />,     <orderEntry type="library" name="groovy-3.0.9" level="application" />,   </component>, </module>]

第四种读取结果(读取前100个字符):

<?xml version="1.0" encoding="UTF-8"?>
<module type="JAVA_MODULE" version="4">
  <component name="Ne
  • 写文件 写入文件也相对简单,这里只写两个简单的例子。
def file = new File("/Users/sherry/IdeaProjects/HelloWorldGroovy/HelloWorldGroovy.iml")
// 在原有的文件后面拼接数据
file.append('123456789')

// 覆盖原本的文件内容
file.withWriter('utf-8') {
    writer.writeLine '123'
    writer.writeLine '456'
    writer.writeLine '789'
}

注释很清晰易懂,所以这里就不作输出结果的展示了,大家可以自己尝试一下。