前言
从今年的4月开始入手Kotlin到现在也有几个月时间了,在Kotlin当中循环算是这个语言当中的一个特点,最近Android上用Canvas在做游戏开发,循环也是写的非常多,发现在编写多变量的For循环无从下手,最后要不就是用Java写Kotlin调用,要不就是用While循环写,终于在今天我是解决了这个痛点,所以就打算写篇文章记录一下。
基本使用
在正式学习之前咱们先从最简单的For循环开始
从0循环到100
until关键字
until关键字在使用时左侧和右侧都需要一个数值,until循环是一个左闭右开区间
for (i in 0 until 100) {
println(i)
}
最终的输出结果包含0不包含100
使用…
…在Kotlin中也是有含义的,它称为区间表达式,它表示一个左闭右闭区间,和until关键字一样在使用的时候左侧和右侧都需要一个数值
for (i in 0..100) {
println(i)
}
最终的输出结果包含0也包含100
补充
其实…关键字也可以在直接声明成一个变量,这个变量就表示一个区间。当循环的时候 循环这个变量就会循环存储在当中的区间
val test = 0..100
注意: 使用…创建的区间只能是一个升序区间,也就是从小到大,不能从大到小,如果想从大到小需要使用另外一个关键字downTo
从100循环到0
downTo关键字
Kotlin当中降序循环使用downTo关键字,一样它也是左右侧都需要一个数值
for (i in 100 downTo 0) {
println(i)
}
你会突然发现为啥是100 ~ 0而不是100 ~ 1,其实早在刚在介绍…关键字的末尾就说了。在降序循环方面downTo关键字生成的是一个左闭右闭区间
step关键字
Kotlin在循环的时候默认步长为1,想要改变步长可以使用step关键字
从0循环到10设置步长为2
for (i in 0..10 step 2) {
print("${if (i == 10) i else "$i--"}")
}
循环数组 or 列表
上述表示都是如何从一个数循环到另外一个数,实际很多情况下用到循环是拿到ArrayList这种容器里面的值。在Kotlin当中循环这些容器的操作都是很简单的
循环一个数组 or 列表
篇幅原因这里就不贴代码结果图了,专家们可以自己拿以下代码去试试
定义一个数组
val arrayList = arrayListOf(1, 2, 3, 4, 5, 6, 12, 31, 23, 123, 12, 3, 123, 2)
直接取出元素
for (i in arrayList) {
println(i)
}
同时循环取出索引 and 元素
同时取出元素的索引需要在循环时调用容器的withIndex()方法
for ((index, item) in arrayList.withIndex()) {
println("index: $index, item: $item")
}
通过索引遍历一个数组 or 列表
有一些情况通过索引遍历,就可以这么写
for (i in 0 until arrayList.size) {
println(arrayList[i])
}
这时候你会发现你的Idea会给你一个黄线警告,因为Kotlin这种情况已经给你提供好了一个扩展方法indices
for (i in arrayList.indices) {
println(arrayList[i])
}
一个小说明
kotlin的for循环其实远不止去遍历数组或列表,基本日常开发用到的数据容器都可以循环遍历(只要这个容器实现了Iterable接口),篇幅原因就不继续了,专家们可以自己去试试
多变量循环
还记得前言里我说的痛点是啥吗,就是这个多变量循环,java中多变量的for循环转化成Kotlin会直接转化成while的语法,但是while会导致代码行数暴增,可读性也不太好,还创建了局部变量。
互相转化
来看一段Java多变量for循环的代码
for (int i = 0, j = 100; i < 100 && j > 0; i++, j--) {
System.out.println("i: " + i + ", j: " + j);
}
这段代码如果让你改成Kotlin的for循环,怎么改?先不急,先转化成kotlin看看转化后是啥样的
var i = 0
var j = 100
while (i < 100 && j > 0) {
println("i: $i, j: $j")
i++
j--
}
这里Kotlin直接给我们转化成了while,弊端呢也说过了,就不赘述了。直接上Kotlin for的代码
for ((i, j) in (0 until 100).zip(100 downTo 0)) {
println("i: $i, j: $j")
}
执行结果你会发现这三个循环输出都是一样的,篇幅原因就不分别上图了,现在就介绍一下Kotlin多变量循环
定义多变量
在Kotlin的for当中呢 in关键字的前面通常用来定义循环的变量,或者用来表示被循环容器的索引or每一项元素,这里不考虑被循环容器,只考虑定义循环的变量。当需要定义多个变量时在 () 内部进行定义,每一位变量通过逗号分隔(英文逗号)。in关键字后面呢就是设置循环区间了
for((i,j) in...){
}
设置循环区间
不知道你有没有发现,其实在kotlin的for循环定义循环变量的时候都没有给它赋予初始值,而是直接指定了它需要循环的区间以及是降序循环还是正序循环,所以上面描述的in循环前面是定义的变量而不是初始值
现在来给上述定义好的循环变量设置循环区间,多个循环区间使用 .zip() 函数进行设置
for ((i, j) in (0 until 10).zip(1 until 10)) {
println("i: $i, j: $j")
}
上述代码含义 i的区间为0 ~ 10的升序,j的区间为1 ~ 10的升序
我们来输出看看结果
没看错吧,i的难道不应该是0 ~ 9吗,为什么到8结束了?别急,咱们先看j的循环区间1 ~ 10,没问题吧 until左闭右开,这里的j循环次数要比i循环次数少一次,所以结束了。你可能会很懵,为什么结束了难道不应该i要输出到9吗,别急,我们同样的代码用java写
for (int i = 0, j = 1; i < 10 && j < 10; i++, j++) {
System.out.println("i:" + i + ", j:" + j);
}
输出结果一致,配合java一起这块代码应该就理解起来没问题了
咱们继续
三个变量 and 四个变量
有时候循环的变量有三个或者四个,这时候你会发现又无从下手了,尝试在zip().zip(),会发现给你报个错误
for((i,j,k) in (0..100).zip(0..100).zip(0..100)){}
Kotlin: Destructuring declaration initializer of type Pair<Pair<Int, Int>, Int> must have a ‘component3()’ function
大意就是你不能这么写,三个变量或四个变量或者更多的时候你需要使用Pair类型的值作为循环变量,Pair是一个二分元组,可以把多个值同时赋给一个变量,或者同时给多个变量赋值,有二分就还有三分元组,三分元组Triple,不过在我的学习过程中没有用上三分,貌似现在的for循环最多也只支持也二分元组 (zip()返回二分元组),如果有不对的地方欢迎知道详情的大佬补充指正,我也会第一时间修改本文章
好了那我们就开始吧
三个变量
其实改变很少,in前面依旧是两个变量,不过其中一个存储的是元组,元组内部存储着值
先上代码
for ((i, jPair) in (1..100).zip((0..100).zip(0..100)) ) {
println("i: $i, $jPair")
}
你会发现调用两个zip函数,好像咱们还没有介绍zip函数的作用蛤,船到桥头自然直,先看输出结果
你会发现输出的后面jPair变成了一个对象,其实这个时候jPair已经成了元组对象,输出对象,对象里面有两个值,我们直接输出是这样的,如果我们想要拿到其中一个值呢?这么写
for ((i, jPair) in (1..100).zip((0..100).zip(0..100))) {
println("i: $i | jPair.firstValue: ${jPair.first} | jPair.secondValue: ${jPair.second}")
}
输出结果:
二分元组只能存储两个值,两个值都可以通过 对象.first and 对象.second来获取,现在我们再来介绍zip。
zip()介绍
Returns a list of pairs built from the elements of this collection and other collection with the same index. The returned list has length of the shortest collection.
上述的英文是在zip()函数的注释中截取下来的,大致意思就是返回索引最小的集合作为索引,两个集合取它们的交集。
是不是看完觉得很懵,没错我也一样,咱们不看注释了直接来看看源码接收的参数还有返回的参数吧
public infix fun <T, R> Iterable<T>.zip(other: Iterable<R>): List<Pair<T, R>> {
return zip(other) { t1, t2 -> t1 to t2 }
}
接收的参数需要是一个实现了Iterable接口的类型,正好在Kotlin中常用的数据存储结构其实都已经实现了这个接口,同时咱们的区间表达式也是支持的。
在看返回值,返回值返回了一个包含元组对象的列表,接下来你就可以再看看三个变量的for循环,是不是一目了然了
四个变量
四个变量就这么写,细节我也不继续赘述了,和三个变量就只有一点点区别而已
for ((iPair, jPair) in ((1..100).zip(1..100)).zip((0..100).zip(0..100))) {
println("iPair: $iPair | jPair: $jPair")
}
执行结果:
要拿取元组对象里面的值上述也讲过了,这里就不在重复了。
小结
总体来说在多变量循环的时候Kotlin还是比较复杂的,但是如果熟悉语法其实看上去还是非常简洁的,不过日常开发中很少会涉及到多变量循环,这里也只是提一嘴其实kotlin是能做到的。如果没有明白就自己写写例子,会更加清楚zip()函数的用法以及for循环在多变量的时候执行流程
循环控制
有时候我们需要对循环进行一些控制,kotlin在这方面也有它的独特之处
结构跳转表达式
和大多数语言一样Kotlin的for循环也是两个结构跳转表达式:
- break : 终止距离最近的循环
- continue : 跳出最近的一次循环
for (i in 0 until 10) {
if (i == 5) break
}
for (i in 0 until 10) {
if (i == 5) continue
}
标签命名
标签命名算是Kotlin 循环当中很有特色的一个点了,我们可以给循环设立一个标签,然后通过结构跳转表达式操作标签对应的循环。
命名格式为: 标签名@
跳出指定标签可以这么写: break@标签名 or continue@标签名
all@ for (i in 0 until 100) {
inner@ for (j in 100 downTo 0) {
if (i == 60) break@inner
if (j == 60) continue@all
println("j: $j")
}
println("i: $i")
}
大家可以自己去试试,这里也就不贴执行结果了
总结
Kotlin的For循环我觉得总结的还算比较到位,从简单使用到多变量、遍历常用的容器,以及循环控制都总结到了。文章中如果你觉得哪里写的有问题欢迎指正,这也是我的第一篇文章,可能一些知识点描述的不是很好,不过我相信配合代码自己动手试试应该很快能理解我想表达的意思。最后谢谢您的阅读!
|