Kotlin基础入门 - forforEach 循环

Posted Modu_Liu

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Kotlin基础入门 - forforEach 循环相关的知识,希望对你有一定的参考价值。

不论身处何方 for循环这种操作都随处可见,鉴于大多数android开发都是从Java转到Kt的,所以我的思路是从Java的使用习惯来讲一些Kt 的for、forEach 循环方式

基础 for循环

for循环 一般作用于list、map数据集合,这里我直接创建了一个只读List集合

  val dataList = listOf("apple", "banana", "orange")
  val numList = listOf(1, 2, 3, 4, 5, 6, 7, 8, 9, 10)

提前了解

  //Java中获取list循环数据的方式
  dataList.get(i)
  //Java中获取数组数据的方式
  dataList[i]

  //在Kt中不论是集合还是数组都用以下方式
  dataList[i]

惯性 for循环

所谓的惯性思维,指的是从Java过渡的常规for循环思想,均是循环获取角标,然后根据角标取对应值;如下

  for (int i = 0; i < list.size(); i++)  

惯性方式1:在 Kt 中,通常使用 start .. end 来代表一段区间 ,如下就是类似Java的写法,定义一个变量,循环区间定义在 0 - List.size - 1,循环时获取list中每个索引的值

    fun basicsFor1(dataList: List<String>) 
        for (i in 0..dataList.size - 1) 
            println("basicsFor1:" + dataList[i])
        
    

惯性方式2:使用Ranges.kt下的until来代表循环区间,从而完成 for循环

    fun basicsFor2(dataList: List<String>) 
        for (i in 0 until dataList.size) 
            println("basicsFor2:" + dataList[i])
        
    

输出结果

 com.example.fordemo                  I  basicsFor1:apple
 com.example.fordemo                  I  basicsFor1:banana
 com.example.fordemo                  I  basicsFor1:orange
 com.example.fordemo                  I  basicsFor2:apple
 com.example.fordemo                  I  basicsFor2:banana
 com.example.fordemo                  I  basicsFor2:orange

有的人可能发现 dataList.size 没有-1,这样会不会造成数组越界?我们看一下 Int 的 until 扩展函数,你会发现在其内部也是封装了区间处理,故使用无忧


进阶 for循环

Look:以下俩种方式都很常用,可根据需求自行选取

进阶方式1:使用Collections .kt下的indices来代表循环区间,从而完成 for循环

    fun basicsFor3(dataList: List<String>) 
        for (i in dataList.indices) 
            println("basicsFor3:" + i)
            println("basicsFor3:" + dataList[i])
        
    

同上疑问,我们看一下 Collection 的 indices扩展属性,你会发现在其内部也是封装了循环区间,故亦无忧

输出结果

 com.example.fordemo                  I  basicsFor3:0
 com.example.fordemo                  I  basicsFor3:apple
 com.example.fordemo                  I  basicsFor3:1
 com.example.fordemo                  I  basicsFor3:banana
 com.example.fordemo                  I  basicsFor3:2
 com.example.fordemo                  I  basicsFor3:orange

进阶方式2:这种方式应该是最快捷的一种循环方式,从循环区间的定义到值的输出都很方便,但是如果需要角标做一些其他操作的话,并不太适合,可用上者~

    fun basicsFor4(dataList: List<String>) 
        for (i in dataList) 
            println("basicsFor4:$i")
        
    

输出结果

 com.example.fordemo                  I  basicsFor4:apple
 com.example.fordemo                  I  basicsFor4:banana
 com.example.fordemo                  I  basicsFor4:orange

倒序 for循环

之前的 for循环 方式都是正序的,在一些场景下我们需要倒序,故此也可以学一学

    /**
     * 倒序
     * */
    fun invertedFor(numList: List<Int>) 
        for (i in numList.size - 1 downTo 0) 
            println("invertedFor:" + numList[i])
        
    

没事儿看看 downTo 的源码,发现也是一个区间定义

输出结果

com.example.fordemo                  I  invertedFor:10
com.example.fordemo                  I  invertedFor:9
com.example.fordemo                  I  invertedFor:8
com.example.fordemo                  I  invertedFor:7
com.example.fordemo                  I  invertedFor:6
com.example.fordemo                  I  invertedFor:5
com.example.fordemo                  I  invertedFor:4
com.example.fordemo                  I  invertedFor:3
com.example.fordemo                  I  invertedFor:2
com.example.fordemo                  I  invertedFor:1

forEach循环

众所周知在Java中提供了forEach循环,同理Kt也支持使用forEach循环;使用时都是通过 list.forEach... 方式,来看一看提供的方法有forEachforEachIndexed,主要区别在于是否需要index角标,其余都是重载方法~

forEach

    fun forEach(dataList: List<String>) 
        dataList.forEach 
            println(it)
        
    

输出结果

com.example.fordemo                  I  apple
com.example.fordemo                  I  banana
com.example.fordemo                  I  orange

也可以直接用 区间+forEach循环,以下为示例...

整数型的集合,可以免创建list

    fun forEach() 
        (0..6).forEach
            println(it)
        
    

输出结果

com.example.fordemo                  I  0
com.example.fordemo                  I  1
com.example.fordemo                  I  2
com.example.fordemo                  I  3
com.example.fordemo                  I  4
com.example.fordemo                  I  5
com.example.fordemo                  I  6

其他类型需要声明对应的集合,可直接声明全局list或传参函数内部

区间 (0..dataList.size - 1) + forEach

    fun forEach(dataList: List<String>) 
        (0..dataList.size - 1).forEach 
            println(dataList[it])
        
    

输出结果

com.example.fordemo                  I  apple
com.example.fordemo                  I  banana
com.example.fordemo                  I  orange

区间(dataList.indices)+ forEach

    fun forEach() 
        (dataList.indices).forEach 
            println(dataList[it])
        
    

输出结果

com.example.fordemo                  I  apple
com.example.fordemo                  I  banana
com.example.fordemo                  I  orange

forEachIndexed

    fun forEachIndexed(dataList: List<String>) 
        dataList.forEachIndexed  index, it ->
            println("角标=$index 元素=$it")
        
    

输出结果

com.example.fordemo                  I  角标=0 元素=apple
com.example.fordemo                  I  角标=1 元素=banana
com.example.fordemo                  I  角标=2 元素=orange

for循环 step过滤

step 本意是跳过的意思,之所以我说是过滤,只因个人感觉step也是过滤数据的一种方式而已

初浅看一下setp函数,发现支持的入参均为整数型,故下方案例我们都传整数

常规开发中 step 跳过的数据,一般是 setp数值*倍数(依次从1,2,3...) os:尝试抛开角标的概念,单纯从第几个数的角度去看待这个场景

string集合

  for (i in dataList.indices step 2) 
      println("stepFor:" + dataList[i])
  

输出结果

com.example.fordemo                  I  stepFor:apple
com.example.fordemo                  I  stepFor:orange

int集合

  for (i in numList.indices step 2) 
      println("stepFor:" + numList[i])
  

输出结果

com.example.fordemo                  I  stepFor:1
com.example.fordemo                  I  stepFor:3
com.example.fordemo                  I  stepFor:5
com.example.fordemo                  I  stepFor:7
com.example.fordemo                  I  stepFor:9

int集合(倒序)

  for (i in numList.size-1 downTo 0 step 2)
      println("stepFor:" + numList[i])
  

输出结果

com.example.fordemo                  I  stepFor:10
com.example.fordemo                  I  stepFor:8
com.example.fordemo                  I  stepFor:6
com.example.fordemo                  I  stepFor:4
com.example.fordemo                  I  stepFor:2

for循环 同时获取index、数据

按照以前的常规方式,上方也有讲过

    fun basicsIndexItemFor(dataList: List<String>) 
        for (index in dataList.indices) 
            println("basicsIndexItemFor-index:" + index + " basicsIndexItemFor-item:" + dataList[index])
        
    

输出结果

com.example.fordemo                  I  indexItemFor-index:0 indexItemFor-item:apple
com.example.fordemo                  I  indexItemFor-index:1 indexItemFor-item:banana
com.example.fordemo                  I  indexItemFor-index:2 indexItemFor-item:orange

kt提供的方式(java可能也有)

    /**
    * 同时获取对应角标(索引)和值,类似map吧
    * */
    fun indexItemFor(dataList: List<String>) 
        for ((index, item) in dataList.withIndex()) 
            println("indexItemFor-index:" + index + " indexItemFor-item:" + item)
        
    

输出结果

com.example.fordemo                  I  basicsIndexItemFor-index:0 basicsIndexItemFor-item:apple
com.example.fordemo                  I  basicsIndexItemFor-index:1 basicsIndexItemFor-item:banana
com.example.fordemo                  I  basicsIndexItemFor-index:2 basicsIndexItemFor-item:orange

for循环 多变量、多列表

kt 中的多列表循环,主要用到了 zip关键字关联集合;这种方式是存在一定不足的,例如当前A列表内部只存在3条数据,B列表数据>3条,最后的结果就是B列表也仅会输出前三条!!!

    /**
     * 多变量、多列表循环
     * */
    fun doubleFor(dataList: List<String>,numList: List<Int>) 
        for ((i, j) in dataList.indices.zip(numList.indices)) 
            println("dataList:" + dataList[i] + " numList:" + numList[j])
        
    

输出结果

com.example.fordemo                  I  dataList:apple numList:1
com.example.fordemo                  I  dataList:banana numList:2
com.example.fordemo                  I  dataList:orange numList:3

kt 提供的这种场景,其实大致等同于以下的循环方式(抛开上面提到的不足)

    fun doubleDataFor(dataList: List<String>) 
        for (i in dataList.indices) 
            println("dataList:" + dataList[i])
        
    

    fun doubleNumFor(numList: List<Int>) 
        for (i in numList.indices) 
            println("numList:" + numList[i])
        
    

输出结果

com.example.fordemo                  I  dataList:apple
com.example.fordemo                  I  dataList:banana
com.example.fordemo                  I  dataList:orange
com.example.fordemo                  I  numList:1
com.example.fordemo                  I  numList:2
com.example.fordemo                  I  numList:3
com.example.fordemo                  I  numList:4
com.example.fordemo                  I  numList:5
com.example.fordemo                  I  numList:6
com.example.fordemo                  I  numList:7
com.example.fordemo                  I  numList:8
com.example.fordemo                  I  numList:9
com.example.fordemo                  I  numList:10

不知道有人会不会有人和我一样懒,既然多列表循环,用的角标值也一样,能不能直接声明一个变量就好... 事实就是提示用Pair类~ 那么什么是Pair类呢?

没事儿做,一起看看zip源码

  • zip 函数,返回的是List <Pair<T, R>
  • Pair类
  • zip 实现

Pair 类+zip 多列表循环

    fun doublePairFor(dataList: List<String>, numList: List<Int>) 
        for (Pair in dataList.indices.zip(numList.indices)) 
            println("dataList&numList:$Pair")
            println("dataList&numList:"+dataList[Pair.first]+"-"+numList[Pair.second])
        
    

输出结果

com.example.fordemo                  I  dataList&numList:(0, 0)
com.example.fordemo                  I  dataList&numList:apple-1
com.example.fordemo                  I  dataList&numList:(1, 1)
com.example.fordemo                  I  dataList&numList:banana-2
com.example.fordemo                  I  dataList&numList:(2, 2)
com.example.fordemo                  I  dataList&numList:orange-3

总结

首先,我写的这些 for循环 都是木有问题的,都循环的很漂亮,知悉一下哈~


Demo 源码

package com.example.fordemo

import android.os.Bundle
import androidx.appcompat.app.AppCompatActivity

class MainActivity : AppCompatActivity() 
    private val dataList = listOf("apple", "banana", "orange")
    private val numList = listOf(1, 2, 3, 4, 5, 6, 7, 8, 9, 10)

    override fun onCreate(savedInstanceState: Bundle?) 
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)

        basicsFor1(dataList)
        basicsFor2(dataList)
        basicsFor3(dataList)
        basicsFor4(dataList)

        invertedFor(numList)
        stepFor()
        indexItemFor(dataList)
        basicsIndexItemFor(dataList)
        doubleFor(dataList, numList)
        doubleDataFor(dataList)
        doubleNumFor(numList)
        doublePairFor(dataList, numList)
        forEach(dataList)
        forEachIndexed(dataList)
    

    /**
     * step
     * */
    private fun stepFor() 
        for (i in dataList.indices step 2) 
            println("stepFor:" + dataList[i])
        
        for (i in numList.indices step 2) 
            println("stepFor:" + numList[i])
        
        for (i in numList.size - 1 downTo 0 step 2) 
            println("stepFor:" + numList[i])
        
    

    private fun forEach(dataList: List<String>) 
        dataList.forEach 
            println(it)
        
    

    private fun forEachIndexed(dataList: List<String>) 
        dataList.forEachIndexed  index, it ->
            println("角标=$index 元素=$it")
        
    

    private fun basicsFor1(dataList: List<String>) 
        for (i in 0..dataList.size - 1) 
            println("basicsFor1:" + dataList[i])
        
    

    private fun basicsFor2(dataList: List<String>) 
        for (i in 0 until dataList.size) 
            println("basicsFor2:" + dataList[i])
        
    

    private fun basicsFor3(dataList: List<String>) 
        for (i in dataList.indices) 
            println("basicsFor3:" + i)
            println("basicsFor3:" + dataList[i])
        
    

    private fun basicsFor4(dataList: List<String>) 
        for (i in dataList) 
            println("basicsFor4:$i")
        
    

    /**
     * 倒序
     * */
    private fun invertedFor(numList: List<Int>) 
        for (i in numList.size - 1 downTo 0) 
            println("invertedFor:" + numList[i])
        
    

    /**
     * 同时获取对应角标(索引)和值,类似map吧
     * */
    private fun indexItemFor(dataList: List<String>) 
        for ((index, item) in dataList.withIndex()) 
            println("indexItemFor-index:" + index + " indexItemFor-item:" + item)
        
    

    private fun basicsIndexItemFor(dataList: List<String>) 
        for (index in dataList.indices) 
            println("basicsIndexItemFor-index:" + index + " basicsIndexItemFor-item:" + dataList[index])
        
    

    /**
     * 多变量、多列表循环
     * */
    private fun doubleFor(dataList: List<String>, numList: List<Int>) 
        for ((i, j) in dataList.indices.zip(numList.indices)) 
            println("dataList:" + dataList[i] + " numList:" + numList[j])
        
    

    private fun doubleDataFor(dataList: List<String>) 
        for (i in dataList.indices) 
            println("dataList:" + dataList[i])
        
    

    private fun doubleNumFor(numList: List<Int>) 
        for Kotlin快速入门

一、函数

 1 /*
 2 *  1.函数可以定义在文件最外层,不需要把它放在类中
 3 *  2.可以省略结尾分号
 4 * */
 5 fun main(args: Array<String>) {
 6     println(max2(1,2))
 7     println(max(3,5))
 8 }
 9 
10 fun max(a: Int, b: Int): Int{
11     return if (a>b) a else b
12 }
13 
14 /*
15 *  与Java不同,Kotlin中,除了循环(for、do和do/while)
16 *  以外大多数控制结构都是表达式,而不是语句(表达式和语句的区别?)。
17 *  所以可以有如下写法。既然有了值(可推断返回类型),就可以省略返回类型。注意,只有
18 *  表达式体函数的返回类型可以省略,对于有返回值的代码块体函数不可省略(这么设计的好处?)。
19 * */
20 fun max2(a: Int, b: Int)=if (a>b) a else b

 

二、变量

 1 fun main(args: Array<String>) {
 2     //声明变量的三种方式
 3     val a="For the Lichking!"
 4     val b: Int=88
 5     val c=8.8e8
 6     val d: String
 7     d="ssss"
 8 
 9    /*
10    * 声明变量的关键字有两个:
11    * val(来自value),不可变引用。相当于Java中的final变量。
12    * var(来自variable),可变引用(不同于Javascript,类型仍然不可变)。
13    * 相当于Java中的普通变量
14    * */
15 
16 
17     /*
18     * 如果编译器能确保只有唯一一条初始化语句被执行,可根据条件使用不同值初始化它。
19     * */
20     val message: String
21     val isTrue: Boolean=true
22     if (isTrue) message="true" else message="false"
23 }

三、字符串模板

1 fun main(args: Array<String>) {
2     //只需要在变量名前加"$"就可以在字符串字面量中引用变量
3     val name=if (args.isNotEmpty()) args[0] else "Kotlin"
4     println("Hello,${name}")
5 
6     //还可以这么玩儿
7     println("Hello,${if (args.isNotEmpty()) args[0] else "NoJava"}")
8 }

四、类

1.java类

 1 public class Person {
 2     private final String name;
 3 
 4     public Person(String name){
 5         this.name=name;
 6     }
 7     
 8     public String getName() {
 9         return name;
10     }
11 }

转换为Kotlin类

1 //这种只有数据没有其他代码的类,通常被叫做值对象
2 class Person(val name: String)

属性:

 1 /*
 2 *  在Java中,字段和其访问器的组合叫做属性。
 3 *  Kotlin的类中声明一个变量就是声明一个属性。
 4 * */
 5 class Man(
 6         val name:String, //只读属性:生成字段和其getter
 7         var isMarried:Boolean) //可写属性:生成字段和getter、setter
 8 
 9 class Rectangle(val height: Int,val width: Int){
10     val isSquare: Boolean
11     //自定义访问器
12     /*get() {
13         return height==width
14     }*/
15 
16     get() = height==width
17 }
18 
19 fun main(args: Array<String>) {
20     val tang=Man("Tang Jiujia",false)
21     println("My name is ${tang.name}")
22 
23     val rectangle=Rectangle(22,33)
24     println(rectangle.isSquare)
25 }

五、目录和包

 1 package gemetry.shapes
 2 
 3 import java.util.*
 4 
 5 class Rectangle(val height:Int,val width: Int){
 6     val isSquare: Boolean
 7         get() = height==width
 8 }
 9 
10 //顶层函数
11 fun createRectangle(): Rectangle{
12     val random = Random()
13     return Rectangle(random.nextInt(),random.nextInt())
14 }
15 
16 //Kotlin中,可以把多个类放在同一个文件中,文件的名字还可以随意取
17 //因此可以不创建shapes文件夹,直接用shapes文件代替文件夹。
18 //当然,坚持Java的目录文件结构仍然是个不错的选择
19 class RectangleUtil()
1 package gemetry.example
2 
3 //可以这样导入顶层函数,当然使用"*"时顶层函数也会被导入
4 import gemetry.shapes.createRectangle
5 
6 fun main(args: Array<String>) {
7     println("Height: ${createRectangle().height}"+"\n"+"Width: ${createRectangle().width}")
8 }

六、表示和处理选择:枚举和when

 1 package gemetry.enums
 2 
 3 enum class Color(val r: Int,val g: Int,val b:Int){
 4     RED(255,0,0),ORANGE(255,165,0),YELLOW(255,255,0),
 5     GREEN(0,255,0),BLUE(0,0,255); //这里分号不能省略,如果要在枚举类
 6     //中定义任何方法,必须用分号把常量列表和方法分开。
 7 
 8     //同Java一样,可以在枚举类中声明属性和方法
 9     fun rgb()=(r*256+g)*256+b
10 }
11 
12 fun getMc(color: Color)=
13         when (color){
14             Color.BLUE -> "blue"
15             //不写"break"
16             //可以多个情况一起
17             Color.GREEN,Color.RED -> "green or red"
18             //必须把所有的可能的情况都列出来,不然就用"else"
19             Color.YELLOW,Color.ORANGE ->"others"
20         }
21 
22 fun mix(c1: Color,c2: Color)=
23         //与Java中的switch只能使用常量(枚举常量、字符串、数字字面量)
24         //不同,when允许使用任何对象,任何表达式
25         when (setOf<Color>(c1,c2)){
26             setOf(Color.RED,Color.YELLOW) -> "orange"
27             setOf(Color.YELLOW,Color.BLUE) ->"GREEN"
28             setOf(Color.BLUE,Color.RED) -> "unknow"
29             else -> "???"
30         }
31 
32 fun mix2(c1: Color,c2: Color)=
33         //不带参数的when
34         when{
35             (c2==Color.RED && c2==Color.YELLOW) ||
36                     (c1==Color.YELLOW && c2==Color.RED) -> "orange"
37             else -> "others"
38         }
39 
40 fun main(args: Array<String>) {
41     println(getMc(Color.ORANGE))
42     println(mix(Color.YELLOW,Color.BLUE))
43     println(mix2(Color.RED,Color.GREEN))
44 }

 七、智能转换和“代码块最后表达式就是结果”

 1 package gemetry
 2 
 3 
 4 interface Expr
 5 class Num(val value: Int) : Expr //实现了Expr
 6 class Sum(val left: Expr,val right: Expr) : Expr
 7 /*
 8 fun eval(e: Expr) : Int=
 9         if (e is Num) e.value
10         else if (e is Sum) eval(e.right)+ eval(e.left)
11         else throw IllegalArgumentException("Unknown expression")*/
12 
13 fun eval(e: Expr): Int=
14         //when表达式不仅限于检查值是否相等,也可用于检测when实参类型。
15         when(e){
16             is Num -> e.value //"is" 判断后自动转换为该类型
17         // (被判断的属性必须是一个val属性,而且不能有自定义访问器)
18             is Sum -> eval(e.right) + eval(e.left)
19             else -> throw IllegalArgumentException("Unknown expression")
20         }
21 
22 fun evalWithLogging(e: Expr) : Int=
23         when(e){
24             is Num -> {
25                 println("num: ${e.value}")
26                 e.value //代码快的最后一个表达式就是结果
27                 /*
28                 * 规则-----“代码块中最后的表达式就是结果”,在所有使用代码块并期望得到一个
29                 * 结果的地方成立。但是,这个规则对常规函数不成立。一个函数要么具有不是代码
30                 * 块的表达式函数体,要么具有包含显示return语句的代码块函数体。
31                 * */
32             }
33 
34             is Sum -> {
35                 val left= evalWithLogging(e.left)
36                 val right= evalWithLogging(e.right)
37                 println("sum: $left+$right")
38                 left+right
39             }
40             else -> {
41                 throw IllegalArgumentException("Unknown Expression")
42             }
43         }
44 
45 fun main(args: Array<String>) {
46     println(eval(Sum(Sum(Num(1),Num(3)),Num(3))))
47     println(evalWithLogging(Sum(Sum(Num(1),Num(3)),Num(3))))
48 }

八、迭代

1.when 同Java中一样

2.for: 仅以for-each循环的形式存在 for <item> in <elements>

3.while:同Java

4.区间:为了代替Java中的普通for循环。区间本质就是值之间的间隔,这两个值通常是数字:一个起始值,一个结束值。使用..运算符表示。

能迭其所有值的区间,叫做数列。

1 val i: Int=0
2     for (i in 1..10){
3         println(i)
4     }
1 val i: Int=0
2     //反向迭代,步长为2
3     for (i in 20 downTo 1 step 2) println(i)

5.迭代map

 1 fun main(args: Array<String>) {
 2     val binaryReps=TreeMap<Char,String>()
 3 
 4     for (c in ‘A‘..‘F‘) { //区间迭代字符
 5         val binary=Integer.toBinaryString(c.toInt())
 6         binaryReps[c]=binary //根据键来更新map的简明语法
 7     }
 8     
 9     for ((l,b) in binaryReps) println("$l=$b") //迭代map
10 }

6.使用in来检查值是否在区间中

1 fun isLetter(c: Char)= c in ‘a‘..‘z‘ || c in ‘A‘..‘Z‘
2 fun isNotDigit(c: Char)=c !in ‘0‘..‘9‘ //不在区间中
println("Kotlin" in "Java".."Scala")//字符串是按字母顺序表比较
    // (因为String就是这样实现Comparable接口的)

九、异常

1.Kotlin并不区分受检异常和非受检异常,因此函数可以不抛出特定异常,异常可以被处理或者不处理。

2.“try”可以作为一个表达式。

以上是关于Kotlin基础入门 - forforEach 循环的主要内容,如果未能解决你的问题,请参考以下文章

Kotlin基础Kotlin快速入门

Kotlin快速入门基础

Android:Kotlin详细入门学习指南-高阶函数-基础语法

Android:Kotlin详细入门学习指南-高阶函数-基础语法

Android:Kotlin详细入门学习指南-基础语法

Android:Kotlin详细入门学习指南-类和对象(上)-基础语法