10. Groovy 运算符-展开操作符学习

2022-12-07 18:27:31 浏览数 (1)

1. 介绍

Groovy语言学习的第10篇内容,继续分享关于运算符的相关知识点。在之前几篇中分享了很多运算符。今天继续分享新的运算符:展开操作符。

也有叫做扩展运算符的。

下面将使用通俗易懂的方式和代码给大家介绍展开操作符的基本使用和特性。

我的所有示例代码。都可以直接在VS 编译器中,直接运行的。通过Groovy插件 Code Runner 插件。(本地环境配置了Groovy SDK地址,Java SDK 地址)

2. 展开操作符-Spread operator

展开操作符的运算符号为:*. 用于调用聚合对象(例如,集合List对象)的所有项的操作。这相当于对每个项目调用操作并将结果收集到列表中,示例如下:

代码语言:javascript复制
//创建了一个Demo类,两个属性值。
class Demo{
    String name
    String address
}

//创建了一个集合对象。
def demo1=[
    new Demo(name:'zinyan.com',address:'湖北'),
    new Demo(name:'Z同学',address:'武汉')]
//获取分布操作,只取集合中的元素的name值。
def makes =demo1*.name
println(makes)  //将会输出 [zinyan.com, Z同学]

通过这个示例,是不是很简单就可以将集合中的元素的某个值给获取到了。这就是该操作符的用处。

我们执行的*.name 操作 实际上是调用了demo1.collect{it.make}而已。

同时,展开操作符也是一个Null安全操作符。也就是说使用这个运算符不会出现NullPointerException异常,如果集合中某个对象为Null,它将返回的对象也是Null。示例如下:

代码语言:javascript复制
//创建了一个集合对象。
def demo1=[
    new Demo(name:'zinyan.com',address:'湖北'),
    null,null,
    new Demo(name:'Z同学',address:'武汉')]
//获取展开操作,只取集合中的元素的name值。
def makes =demo1*.name
println(makes)  //[zinyan.com, null, null, Z同学]

除此之外,如果集合对象是个Null的情况下调用*.也是安全的,不会出现NullPointerException异常。示例如下:

代码语言:javascript复制
def zinyan
println(zinyan*.name)  //输出 null

同时,该运算符也可以用于实现了Iterable接口的任何类,示例代码如下:

代码语言:javascript复制
//创建了一个Demo类,两个属性值。
class Demo{
    String name
    String address
}
//我创建一个集成了Iterable接口的类。
class DemoZ implements Iterable<Demo>{
    //创建一个集合对象,在实际使用中,我们可以动态接口复制给它
   def demo1=[
    new Demo(name:'zinyan.com',address:'湖北'),
    new Demo(name:'Z同学',address:'武汉')]

    //重构iterator接口
    @Override
    Iterator<Demo> iterator() {
        demo1.iterator()
    }
}

//示例效果
def demoz = new DemoZ()
println(demoz*.name) //把所有的name值打印出来 输出:[zinyan.com, Z同学]

上面的List都是一级目录的情况。如果List是二级甚至多级嵌套的情况下,也一样可以使用分布操作符*.来实现某个数据参数的聚合。示例代码如下:

代码语言:javascript复制
//创建了一个Demo类,两个属性值。
class Demo{
    String name
    String address
}

class DemoList{
    String name
    List<Demo> demos 
}

def demo1=[ 
    new DemoList(name:"第一个集合",demos:[
                    new Demo(name:'zinyan.com',address:'湖北'),
                    new Demo(name:'Z同学',address:'武汉')]
                    ),
     new DemoList(name:"第二个集合",demos:[
                    new Demo(name:'zin',address:'湖北'),
                    new Demo(name:'yan',address:'武汉')]
                    )
        ]
def x = demo1*.name
def y =demo1*.demos*.name
println(x)  // 输出内容 :[第一个集合, 第二个集合]
println(y)  //输出内容:[[zinyan.com, Z同学], [zin, yan]]
//可以看到y得到的数据是按照原有结构存储的,是一个二维数组。
def z = y.flatten() //展平所有级别
println(z)  //输出内容为:[zinyan.com, Z同学, zin, yan]

flatten()方法类似的还有sum()方法。在上面的示例中,都可以达到将多维数组结果转为一维数组。 这里只是简单介绍一下,后面分析的博客中会有相关的方法介绍。

但是,对于多维集合的情况,Groovy建议我们使用collectNested方法而不是展开操作符,上面的是嵌套列表,不是多维列表哦。

示例如下:对于一个二维集合对象的使用:

代码语言:javascript复制
//创建了一个Demo类,两个属性值。
class Demo{
    String name
    String address
}

def demo1=[ 
    [new Demo(name:'zinyan.com',address:'湖北'),
     new Demo(name:'Z同学',address:'武汉')],
    [new Demo(name:'zin',address:'湖北'),
     new Demo(name:'yan',address:'武汉')]
     ]
def x= demo1.collectNested{
    it.name
}
println(x)  //输出内容为:[[zinyan.com, Z同学], [zin, yan]]
def z = x.flatten() //展平所有级别
println(z)  //输出内容为:[zinyan.com, Z同学, zin, yan]

我们除了在List中使用,也可以在Map中使用,例如快速获取Map的Key值或者Values值列表:

代码语言:javascript复制
def map =[name:'zinyan',address:'中国湖北',alias:'Z同学']
println(map)  //输出内容为:[name:zinyan, address:中国湖北, alias:Z同学]
def x = map*.key
println(x)  //输出内容为:[name, address, alias]
def y = map*.value
println(y)  //输出内容为:[zinyan, 中国湖北, Z同学]

2.1 扩展方法参数

在某些情况下,可以在列表中找到方法调用的参数,而且需要调整这些参数。在这种情况下,可以使用展开操作符的扩展方法参数来实现。示例如下:

代码语言:javascript复制
//有一个方法,通过入值进行计算并返回一个int数据。
int function(int x, int y, int z) {
    x*y z
}
//我们有一个集合
def args = [4,5,6]
//我们可以直接使用
def val = function(*args)
println(val)  //输出为:26

在上面示例中。计算结果为 4*5 6 = 26 操作符会自动按照顺序读取参数用于计算。

而如果集合参数不够会怎么样?集合参数过多又会怎么样?让我们通过示例了解:

代码语言:javascript复制
//有一个方法,通过入值进行计算并返回一个int数据。
int function(int x, int y, int z) {
    x*y z
}
//我们有一个集合
def args = [4,5,6,7]
//我们可以直接使用 ,但是集合数据超过了 function的入参数量。
def val = function(*args)
println(val)

上面的示例中,集合的数据过多了。我们传入进去后就会出现下面的错误:

代码语言:javascript复制
Caught: groovy.lang.MissingMethodException: No signature of method: groovy.function() is applicable for argument types: (Integer, Integer, Integer, Integer) values: [4, 5, 6, 7]
Possible solutions: function(int, int, int)
groovy.lang.MissingMethodException: No signature of method: groovy.function() is applicable for argument types: (Integer, Integer, Integer, Integer) values: [4, 5, 6, 7]
Possible solutions: function(int, int, int)
    at groovy.run(groovy.groovy:8)

所以,不能使用过高的。那么如果过少?示例如下:

代码语言:javascript复制
//有一个方法,通过入值进行计算并返回一个int数据。
int function(int x, int y, int z) {
    x*y z
}
//我们有一个集合
def args = [4,5]
//我们可以直接使用 ,但是集合数据超过了 function的入参数量。
def val = function(*args,6)
println(val)  //输出为:26

我们需要主动补上缺少的参数。否则会和过多的是一样的错误

2.2 列表扩展

上面都是在集合外的一些使用,我们也可以将展开操作符用于集合对象的创建中来。示例如下:

代码语言:javascript复制
def x1 =['zin','yan','com']
def x2 =['zinyan.com',*x1,'z同学'] //将集合x1直接插入x2集合中来
println(x2)  //输出:[zinyan.com, zin, yan, com, z同学]

当在列表中使用时,展开运算符的作用就像扩展元素内容内联到列表中

不只是List中,在Map中也是一样的。只是写法有一些差异,示例代码如下:

代码语言:javascript复制
def x1 =[a:'zin',b:'yan',c:'com']
def x2 =[a:'zinyan',d:'Z同学',*:x1] //将集合x1直接插入x2Map中来
println(x2)  //输出:[a:zin, d:Z同学, b:yan, c:com]

可以看到,上面的示例中x1有key值和x2重复了。将x1插入到x2后,x2中本身的值进行了替换。

我们如果想让x2的值是最新值。那么就需要将x1的插入顺序进行调整了。示例如下:

代码语言:javascript复制
def x1 =[a:'zin',b:'yan',c:'com']
def x2 =[*:x1,a:'zinyan',d:'Z同学'] //将集合x1直接插入x2Map中来
println(x2)  //输出:[a:zinyan, b:yan, c:com, d:Z同学]

Ps:不管是List中的扩展,还是Map中的扩展。插入的位置是会明显影响最后的结果的。

3. 小结

到这里,有关于展开操作符*.的介绍就结束了。总的来说就是针对聚合对象的一种便捷写法,可以用尽量少的代码实现一些集合的操作。

Groovy官方文档的链接地址:http://docs.groovy-lang.org/docs/groovy-4.0.6/html/documentation/#_spread_operator

ps:我觉得我介绍的比官方介绍的更容易理解。哈哈 。

0 人点赞