---
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}
```
**--實作結果--**
> 
:::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)}")
}
```
> 
:::
:::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` 檔案
> 
:::
1. **呼叫 `sum` Lambda 函數**:可以看到它調用了 Function2#invoke 方法(`Function2` 代表帶兩個參數的函數)
> 
2. 而 `sum` 函數則實作了 **Function2** 介面
> 
:::danger
* Lambda 會實現 `Function<N>` 接口,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()
}
```
**--實作結果--**
> 
## 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")
}
}
```
**-實作結果--**
> 
### 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]}")
}
}
```
**--實作結果--**
> 
### 集合函數式 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")
}
}
```
> 
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")
}
}
```
> 
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")
}
```
> 
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")
}
```
> 
## 更多的 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`