---
# System prepended metadata

title: Kotlin 函數編程 & 集合
tags: [Kotlin]

---

---
title: 'Kotlin 函數編程 & 集合'
disqus: kyleAlien
---

Kotlin 函數編程 & 集合
===

## Overview of Content

以下參考，第一行代碼 (第三版), Kotlin 進階實戰

如有引用參考本文章請詳註出處，感謝 :smile:

:::success
* 如果喜歡讀更好看一點的網頁版本，可以到我新做的網站 [**DevTech Ascendancy Hub**](https://devtechascendancy.com/)
    
    本篇文章對應的是 [**Kotlin Lambda 編程 & Bytecode | Array & Collections 集合 | 集合函數式 API**](https://devtechascendancy.com/kotlin-lambda-bytecode-array-collections-functional/)
:::

[TOC]


## Lambda
Kotlin 從第一版開始就支持 Lambda 編程，並且 Lambda 在 Kotlin 的功能極為強大，這裡會先以基礎為例，還有其他高階函數、DSL...等等

### Lambda & Kotlin: Step by Setp
* 這裡的重點是學習 **函數式 API 的++語法結構++，就是 Lambda 表達式在 Kotlin 中的運用，==Lambda 可以想做把函式當作參數傳入==**

    先來看看 Lambda 表達式在 Kotlin 中的語法
    ```kotlin=
    /**
     * 1. 最外面大括號
     * 2. 參數列表的尾端使用 `->` 符號
     * 3. 並且 Lambda 最後一行為返回的數值
     */

    {參數名1: 參數類型, 參數名2: 參數類型 -> 函數體}
    ```

    以下在集合中求取最大長度的字串，第一個是傳統的方式，第二個是 Lambda 的使用，之後皆是推導 (不然還真的看不懂)
    ```java=
    package class_3

    val list = listOf<String>("Apple", "Banana", "Orange", "Pear", "Grape", "Watermelon")

    fun main() {

        customCompare()

        kotlinLambda()

        kotlinLambda1()
        kotlinLambda2()
        kotlinLambda3()
        kotlinLambda4()
        kotlinLambda5()
    }

    /**
     * 傳統比對方式
     */
    fun customCompare() {
        var maxLength = ""
        for (i in list) {
            if (i.length > maxLength.length) {
                maxLength = i
            }
        }
        println("custom Max Length is: $maxLength")
    }

    /**
     * Lambda...看沒有 ? 往下看
     */
    fun kotlinLambda() {
        // maxBy 函數原型
        // public inline fun <T, R : Comparable<R>> Iterable<T>.maxBy(selector: (T) -> R): T? {...}
        
        val maxLength = list.maxBy { it.length }
        println("lambda Max Length is: $maxLength")
    }

    /**
     * 第一步
     * 依照 Lambda 表達式先創建
     */
    fun kotlinLambda1() {

        val lambda = {i : String -> i.length}
        println("Lambda - 1, lambda: $lambda")                  // 1
        val maxLength = list.maxBy(lambda)

        println("Lambda - 1: $maxLength")
    }

    /**
     * 第二步
     * 不須定義一個 Lambda 變量
     */
    fun kotlinLambda2() {
        val maxLength = list.maxBy( {i: String -> i.length} )   // 2

        println("Lambda - 2: $maxLength")
    }
    /**
     * 第三步
     * 當最後一個參數為 Lambda 時可以將 Lambda 移到 () 外面
     *
     * 第四步
     * 若 Lambda 為唯一一個參數時可以移除 ()
     */
    fun kotlinLambda3() {
    //    val maxLength = list.maxBy() { i: String -> i.length} // 3
        val maxLength = list.maxBy { i: String -> i.length}     // 4

        println("Lambda - 3/4: $maxLength")
    }

    /**
     * 第五步
     * 由於 Kotlin 有自動推倒機制，所以可以省略參數的 :String 類型
     * (參數列表中大多數都不須宣告類型)
     */
    fun kotlinLambda4() {
        val maxLength = list.maxBy { i -> i.length}         // 5
        println("Lambda - 5: $maxLength")
    }

    /**
     * 第六步
     * 若 "只有一個參數" 則可以不用聲明參數，可以使用關鍵字 it
     */
    fun kotlinLambda5() {
        val maxLength = list.maxBy { it.length}             // 6
        println("Lambda - 6: $maxLength")
    }
    ```

    * 以下整理重點步驟
        
        1. **Lambda 原型**
            
            ```kotlin=
            list.maxBy{(i: String -> i.length)}
            ```
            
        2. **當最後一個參數為 Lambda 時可以將 Lambda 移到 `()` 外面**
            
            ```kotlin=
            list.maxBy() {i: String -> i.length}
            ```
            
        3. **若 Lambda 為唯一一個參數時可以移除小括號 `()`**
            
            ```kotlin=
            list.maxBy{i: String -> i.length}
            ```
            
        4. **由於 Kotlin 有自動推倒機制，所以可以省略參數的類型**
            
            ```kotlin=
            list.maxBy{i -> i.length}
            ```
            
        5. **若「只有一個參數」則可以不用聲明參數，可以使用關鍵字 `it`**
            
            ```kotlin=
            list.maxBy{it.length}
            ```

    **--實作結果--**
    > ![](https://i.imgur.com/da21w7b.png)


    :::success
    * 返回一個函數：一般來講程式語言只能返回基礎類型，但是函數式編程的特點就在於，可以將「函數作為值」返回
        
        > `Lambda` 同時也可以作為一個返回值返回


        ```kotlin=
        enum class ControlType {
            ADD,
            SUB
        }

        fun getLambda(type : ControlType) : (Int, Int) -> Int {
            return when(type) {
                ControlType.ADD -> { a, b -> a + b}
                ControlType.SUB -> { a, b -> a - b}
            }
        }

        fun main() {

            val lambda = getLambda(ControlType.ADD)
            println("Add res: ${lambda(1, 2)}")

            val lambda2 = getLambda(ControlType.SUB)
            println("Sub res: ${lambda2(10, 1)}")

        }
        ```
        
        > ![](https://i.imgur.com/UJK7Vph.png)

    :::
    
    :::info
    * Lambda 中「未」使用到的參數建議使用 `_` 取代其參數名稱，這可以增加程式的可讀性！
    :::

### 解析 Kotlin Lambda：ByteCode

* 現在換個角度來看看 Lambda 表達式：先撰寫 Kotlin 一個帶有 Lambda 函數式的程式，再將其編譯，最後查看 `.class` 文件

    ```kotlin=
    fun main() {

        val sum = {
            x: Int, y : Int -> x + y
        }

        println(sum(1, 2))

    }
    ```
    :::success
    * 透過 IDE 提供的 `kotlin Bytecode` 工具查看該文件的 `.class` 檔案
    
        > ![image](https://hackmd.io/_uploads/B1KSfNmap.png)

    :::
    
    1. **呼叫 `sum` Lambda 函數**：可以看到它調用了 Function2#invoke 方法（`Function2` 代表帶兩個參數的函數）

        > ![](https://i.imgur.com/Iep4KUu.png)

    2. 而 `sum` 函數則實作了 **Function2** 介面
        
        > ![](https://i.imgur.com/MUWU0In.png)

    
        :::danger
        * Lambda 會實現 `Function<N>` 接口，N 的大小由參數決定，接收參數越多則 Ｎ 越大

        * **`inline` 內聯函數** 則 ++不需實現++ Function 接口！
        :::



### Kotlin 調用 Java 函數式
* Kotlin 中調用 Java 也可以使用函數式 (Lambda)，但須有一些條件限制 (其實也就是 [**Java 中實現 Lambda 的方法**](https://hackmd.io/gV5_yfM-RHuVX257ur-P6A?view))，**==Java 抽象接口只能有單一個實現方法==**

    ```java=
    // Runnable 原碼
    @FunctionalInterface
    public interface Runnable {
        public abstract void run();
    }

    // Java 使用匿名內部類調用
    public class JavaThread {

        public static void main(String[] args) {
            new Thread(new Runnable() {
                @Override
                public void run() {
                    System.out.println("Hello Java Thread");
                }
            }).start();
        }
    }
    ```
    
    從上面可以看出來　**`Runnable` 這個介面內部只有一個方法（`run`）**，符合 Kotlin 調用函數式 API 的規定
    :::warning
    * **匿名內部類**
        
        匿名內部類必須使用關鍵字 `object`，因為 Kotlin 捨棄了 `new` 關鍵字，所以在創建匿名內部類時要使用 `object`
    :::
    ```kotlin=
    fun main() {
        javaThread1()
        javaThread2()
        javaThread3()
        javaThread4()
    }

    /**
     * 匿名內部類必須使用關鍵字 "object"
     * 因為 Kotlin 捨棄了 new 關鍵字，所以在創建匿名內部類時要使用 object
     */
    fun javaThread1() {
        Thread(object : Runnable {
            override fun run() {
                println("Kotlin use Runnable - 1")
            }
        }).start()
    }

    /**
     * object 可以提醒未實現方法，但 Runnable 就只有一個實現方法而已
     * 所以可以省略 object & run 方法 (因為 Kotlin 可以自動推導知道
     */
    fun javaThread2() {
        Thread(Runnable {
            println("Kotlin use Runnable - 2")
        }).start()
    }

    /**
     * 如果 Java 方法的參數列表中只有一個 Java 抽象接口，則 Kotlin 可以自動推倒
     * 所以可以省略 Runnable
     */
    fun javaThread3() {
        Thread({
            println("Kotlin use Runnable - 3")
        }).start()
    }

    /**
     * 黨參數 Lambda 表達式為最後一個參數時可以省略 ()，只留下 {} 大括號
     */
    fun javaThread4() {
        Thread {
            println("Kotlin use Runnable - 4")
        }.start()
    }
    ```

    **--實作結果--**
    > ![](https://i.imgur.com/dUSQoSz.png)


## Array 數組

```kotlin=
/**
 * arrayOf 是內建函數
 */
fun main() {
    val array = arrayOf(1, 3, 5, 7, 9)

    for (i in array) {
       println("array: $i")
    }

    // 原型: public inline constructor(size: Int, init: (Int) -> T)
    val lambda = {i : Int -> i + 10}    // i 從 0 開始
    val array2 = Array(5, lambda)
    for (i in array2) {
        println("lambda i : $i")
    }

    /**
     * 省略方式
     * 1. 單一參數可以使用 it
     * 2. lambda 最為最後一個參數可以一至 (外)
     */
    val array3 = Array(5) {it + 10}    // i 從 0 開始
    for (i in array3) {
        println("lambda i --- 2 : $i")
    }
}
```

**-實作結果--**
> ![](https://i.imgur.com/Z1gOUki.png)


### Collections 集合


* 在 Java 中的集合有 List、Set、Map，它們的實現如下 (列舉出常用的)

    | 集合 | 實現 |
    | -------- | -------- |
    | List | ArrayList、LinkedList |
    | Set | HashSet |
    | Map | HashMap |

* Kotlin 對於集合的創建有簡化的內置函數

    | 集合內置函數 | 說明特性 |
    | -------- | -------- |
    | listOf | 初始化時就必須決定集合內容，**並且該集合 ++內容不可更改的++** |
    | mutableListOf | mutable 關鍵字如同 C++ 的關鍵字，說明該 **集合內容可以改變** |
    | setOf | 如同 list 集合，同樣是不可更改的集合 |
    | mutableSetOf | 可以更改的 Set 集合 |
    | mapOf | 不可改變的 map 集合，**Kotlin 不建議使用 put、get，改用指標下定的方式** |
    | mutableMapOf | 可更改的 Map 集合，並使用 infix 函數 |

    ```kotlin=
    fun main() {
        customArray()

        kotlinList()
        kotlinListChange()

        kotlinSet()
        kotlinSetChange()

        customMap()

        kotlinIndex()
        kotlinMap()
        kotlinMapChange()

        forListWithIndex()
    }

    /**
     * Kotlin 可以實現如同 Java 的 List
     */
    fun customArray() {
        val list = ArrayList<Int>()
        list.add(123)
        list.add(456)
        list.add(789)
        println("custom list: $list")
    }

    /**
     * Kotlin 有專屬的 "內置" 函數 listOf (不可更改 !!!
     * 用來簡化 list 的初始化創建
     */
    fun kotlinList() {
        val list = listOf<String>("Apple, Banana, Car")
    //    list[0] = "Bpple";    Error: listOf 內容不可更改，內部元素都是 val
    //    list.add();               也不可以新增元素
        println("listOf: $list")
    }

    /**
     * mutableListOf 是一個可以更改內容的集合
     */
    fun kotlinListChange() {
        val list = mutableListOf<String>("Hello", "World")
        println("mutableListOf before change: $list")
        list[1] = "Kotlin"      // Okay: mutableListOf 內部元素可以更改
        list.add("Go to Lean~") //  也可以新增內容
        println("mutableListOf after change: $list")
    }


    fun kotlinSet() {
        val set = setOf<String>("AAA", "BBB", "CCC")
        for ( i in set) {
            println("setOf element: $i")
        }
    }

    fun kotlinSetChange() {
        val set = mutableSetOf("DDD")
        set.add("EEE")
        set.add("FFF")
        for ( i in set) {
            println("mutableSetOf element: $i")
        }
    }

    fun customMap() {
        val map = HashMap<String, Long>()
        map.put("Alien", 9527L)
        map.put("Pan", 3344L)
        map.put("Kyle", 5566L)
        println("custom Map: $map")
    //    for (i in map) {      // 遍歷 Okay
    //        println("key: ${i.key}, value: ${i.value}")
    //    }
    }

    fun kotlinIndex() {
        val map = HashMap<String, Long>()
        map["Alien"] = 9527L
        map["Pan"] = 3344L
        map["Kyle"] = 5566L
        println("custom Map use index: $map")
    }

    /**
     * to 是一個 index 函數
     */
    fun kotlinMap() {
        val map = mapOf<Char, Int>('A' to 1, 'B' to 2, 'C' to 3)
    //    map['A'] = 10     Error 不可賦值
        println("mapOf: $map")
    }

    fun kotlinMapChange() {
        val map = mutableMapOf<Char, Int>('C' to 4)
        map['D'] = 5
        map['E'] = 6
        println("mutableMapOf: $map")
    }

    /**
     * 要取得 index 必須使用 集合.indices
     */
    fun forListWithIndex() {
        val list = listOf<Char>('A', 'B', 'C')
        list.forEach { a -> println(a) }

        for (index in list.indices) {
            println("index: $index, value: ${list[index]}")
        }
    }
    ```

    **--實作結果--**
    > ![](https://i.imgur.com/lOXhkfq.png)



### 集合函數式 API：map、filter、any、all

* 以下會提及幾個常用的集合 API，其接收參數就是使用 Lambda 

    1. **集合的 `map` 函數**：它會將集合中的每一個元素都套用傳入的 Lambda，並產生一個新的值，最終生成新的集合

        ```kotlin=
        fun main() {
            val list = listOf(100, 200, 300, 400, 500)

            collectionMap(list)
        }

        fun collectionMap(list:List<Int>) {
            // map 會產生新集合
            val newList = list.map { it/100 }
            for (i in newList) {
                println("list2 : $i")
            }
        }
        ```
        > ![](https://i.imgur.com/0u0xHdz.png)


    2. **集合的 `filter` 函數**：同樣 Lambda 會對集合內的每一個元素作用，最後產生一個符合條件的新集合

        ```kotlin=
        fun main() {
            val list = listOf(100, 200, 300, 400, 500)

            collectionMap(list)
            collectionFilter(list)
        }

        fun collectionFilter(list: List<Int>) {
            val newList = list.filter { it in 300..400 }    // 區間
            for (i in newList) {
                println("list filter : $i")
            }
        }
        ```
        > ![](https://i.imgur.com/W2s1ZNX.png)


    3. **集合的 `any` 函數**：判斷集合內任意的元素是否有符合條件

        ```kotlin=
        fun main() {
            val list = listOf(100, 200, 300, 400, 500)

            collectionMap(list)
            collectionFilter(list)
            collectionAny(list)
        }

        fun collectionAny(list: List<Int>) {
            val isPass = list.any { it > 200}
            println("list any: $isPass")
        }
        ```
        > ![](https://i.imgur.com/KUNEu7T.png)


    4. **集合的 `all` 函數**，判斷集合內是否每一個元素都符合條件

        ```kotlin=
        fun main() {
            val list = listOf(100, 200, 300, 400, 500)

            collectionMap(list)
            collectionFilter(list)
            collectionAny(list)
            collectionAll(list)
        }

        fun collectionAll(list: List<Int>) {
            val isPass = list.all { it > 200}
            println("list all: $isPass")
        }
        ```
        > ![](https://i.imgur.com/qLUoU4q.png)

    


## 更多的 Kotlin 語言相關文章

在這裡，我們提供了一系列豐富且深入的 Kotlin 語言相關文章，涵蓋了從基礎到進階的各個方面。讓我們一起來探索這些精彩內容！

### Kotlin 語言基礎

* **Kotlin 語言基礎**：想要建立堅實的 Kotlin 基礎？以下這些文章將帶你深入探索 Kotlin 的關鍵基礎和概念，幫你打造更堅固的 Kotlin 語言基礎

    :::info
    * [**Kotlin 函數、類、屬性 | DataClass、Sealed、Object 關鍵字 | Enum、Companion、NPE**](https://devtechascendancy.com/kotlin-functions_oop_dataclass_sealed_object/)
        
    * [**深入探究 Kotlin 與 Java 泛型：擦除、取得泛型類型、型變、投影 | 協變、逆變**](https://devtechascendancy.com/explore-kotlin-java-generics_type_erasure/)

    
    * [**深入 Kotlin 函數特性：Inline、擴展、標準函數全解析 | 提升程式碼效能與可讀性**](https://devtechascendancy.com/kotlin_inline_extensions_standards-func/)

    * [**Kotlin DSL、操作符、中綴表達式 Infix | DSL 詳解 | DSL 設計與應用**](https://devtechascendancy.com/kotlin-dsl-operators-infix-explained/)
    :::

### Kotlin 特性、特點

* **Kotlin 特性、特點**：探索 Kotlin 的獨特特性和功能，加深對 Kotlin 語言的理解，並增強對於語言特性的應用

    :::warning
    * [**Kotlin 代理與懶加載機制：使用、lazy 深度解析**](https://devtechascendancy.com/kotlin-delegate_java-proxy_lateinit_lazy/)
    
    * [**Kotlin Lambda 編程 & Bytecode | Array & Collections 集合 | 集合函數式 API**](https://devtechascendancy.com/kotlin-lambda-bytecode-array-collections-functional/)
    
    * [**深入理解 Kotlin：智能推斷與 Contact 規則**](https://devtechascendancy.com/kotlin-smart-inference-contract-rules-guide/)
    :::

### Kotlin 進階：協程、響應式、異步

* **Kotlin 進階：協程、響應式、異步**：若想深入學習 Kotlin 的進階主題，包括協程應用、Channel 使用、以及 Flow 的探索，請查看以下文章

    :::danger

    * [**應用 Kotlin 協程：對比 Thread、創建協程、任務掛起 | Dispatcher、CoroutineContext、CoroutineScope**](https://devtechascendancy.com/applied-kotlin-coroutines-in-depth-guide/)
    
    * [**Kotlin Channel 使用介紹 | Select、Actor | 生產者消費者**](https://devtechascendancy.com/kotlin-channel_select_actor_cs/)

    * [**探索 Kotlin Flow：基本使用、RxJava 對比、背壓機制 | Flow 細節**](https://devtechascendancy.com/kotlin-flow-usage_compare-rx_backpressure/)
    :::




## Appendix & FAQ

:::info
:::

###### tags: `Kotlin`
