Groovy之集合容器

ProjectDaedalus

共 7023字,需浏览 15分钟

 · 2022-05-19

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

abstract.png

Array 数组

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

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

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,77as int[]
        assert array2 instanceof int[]
        assert array2.size() == 3

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

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

List 列表

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

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 += [truefalse]
        assert myList4 == [996"Aaron"truefalse]
        // 从列表中移除元素
        myList4 -= "Aaron"
        // 从列表中移除集合
        myList4 -= [true996]
        assert myList4 == [false]
        // 向元素尾部追加元素
        myList4 << "Bob" << 17
        assert myList4 == [false"Bob"17]
    }
}

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

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列表中原有的方法进行了拓展、增强。具体如下所示

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 == [13222491315]
        // 去重同时保留顺序
        def list5 = list3.unique()
        assert list5 == [12213241539]

        // 正向遍历
        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方法

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。基本操作方式示例如下所示

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 == [10021001103519961007]

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

        // 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:18Tom:23]
    }
}

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

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循环不停递增变量的编程方式。示例代码如下所示

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() == [432]
    }
}

参考文献

  1. Groovy In Action · 2nd Edition   Dierk König、Guillaume Laforge著


浏览 23
点赞
评论
收藏
分享

手机扫一扫分享

举报
评论
图片
表情
推荐
点赞
评论
收藏
分享

手机扫一扫分享

举报