Kotlin基礎——Lambda和函數式編程

Lambda

使用 { } 定義Lamba,調用run運行

run { println(1) }

更常用的為 { 參數 -> 操作 },還可以存儲到變量中,把變量作為普通函數對待

val sum = { x: Int, y: Int -> x + y }
println(sum(1, 2))

maxBy()接收一個Lambda,傳遞如下

class Person(val name: String, val age: Int)
val people = listOf(Person("A", 18), Person("B", 19))println(people.maxBy({ p: Person -> p.age }))
println(people.maxBy() { p: Person -> p.age })println(people.maxBy { p: Person -> p.age })	//只有一個參數,可省略()
println(people.maxBy() { p -> p.age })		//可推導出類型
println(people.maxBy() { it.age })		//只有一個參數且可推導出類型,會自動生成it

Lambda可使用函數參數和局部變量

 fun printProblemCounts(response: Collection<String>) {var clientErrors = 0var serverErrors = 0response.forEach {if (it.startsWith("4")) {clientErrors++} else if (it.startsWith("5")) {serverErrors++}}}

成員引用

上面通過Lambda將代碼塊作為參數傳遞給函數,若要傳遞代碼塊已被封裝成函數,則需要傳遞一個調用該函數的Lambda,如下計算虛歲

class Person(val name: String, val age: Int) {fun getNominalAge(): Int {return age + 1}
}val people = listOf(Person("A", 18), Person("B", 19))
println(people.maxBy { p: Person -> p.getNominalAge() })println(people.maxBy(Person::getNominalAge))		//成員引用,可以省略多余的函數調用

上面是系統為fun getNominalAge()自動生成的成員引用,實際定義應該如下,把函數轉換為一個值,從而可以傳遞它

  • 把函數age + 1傳遞給getNominalAge,通過Person::getNominalAge引用函數
  • 直接通過Person::age引用成員
 class Person(val name: String, val age: Int) {val getNominalAge = age + 1}val people = listOf(Person("A", 18), Person("B", 19))println(people.maxBy(Person::getNominalAge))println(people.maxBy(Person::age))

若引用頂層函數,則可以省略類名稱,以::開頭

fun salute() = println("Salute")run(::salute)

集合的函數式API

filter和map

filter遍歷集合并篩選指定Lambda返回true的元素,如下遍歷偶數

val list = listOf(1, 2, 3, 4)
println(list.filter { it % 2 == 0 })data class Person(val name: String, val age: Int)
val people = listOf(Person("A", 29), Person("B", 31))
println(people.filter { it.age > 30 })

map對集合中的每一個元素應用給定的函數并把結果收集到一個新的集合

val list = listOf(1, 2, 3, 4)
println(list.map { it * it })data class Person(val name: String, val age: Int)
val people = listOf(Person("A", 29), Person("B", 31))
println(people.map(Person::name))

Lambda會隱藏底層操作,如尋找最大年齡,第一種方式會執行100遍,應該避免

data class Person(val name: String, val age: Int)
val people = listOf(Person("A", 29), Person("B", 31))
people.filter { it.age == people.maxBy(Person::age)!!.age }val maxAge = people.maxBy(Person::age)!!.age
people.filter { it.age == maxAge }

對于Map,可調用filterKeys/mapKeys、filterValues/mapValues

val numbers = mapOf(0 to "zero", 1 to "one")
println(numbers.mapValues { it.value.toUpperCase() })

all、any、count、find

all判斷集合所有元素是否都滿足條件,any判斷至少存在一個滿足條件的元素

data class Person(val name: String, val age: Int)
val people = listOf(Person("A", 26), Person("B", 27))val max27 = { p: Person -> p.age >= 27 }
println(people.all(max27))
println(people.any(max27))

!all()表示不是所有符合條件,可用any對條件取反來代替,后者更容易理解

val list = listOf(1, 2, 3)
println(!list.all { it == 3 })
println(list.any { it != 3 })

count用于獲取滿足條件元素的個數,其通過跟蹤匹配元素的數量,不關心元素本身,更加高效,若使用size則會創建臨時集合存儲所有滿足條件的元素

data class Person(val name: String, val age: Int)
val people = listOf(Person("A", 26), Person("B", 27))
val max27 = { p: Person -> p.age >= 27 }println(people.count(max27))
println(people.filter(max27).size)

find找到一個滿足條件的元素,若有多個則返回第一個,否則返回null,同義函數firstOrNull

data class Person(val name: String, val age: Int)
val people = listOf(Person("A", 26), Person("B", 27))
val max27 = { p: Person -> p.age >= 27 }println(people.find(max27))
println(people.firstOrNull(max27))

groupby

groupby把列表轉成分組的map

data class Person(val name: String, val age: Int)val people = listOf(Person("A", 26), Person("B", 27), Person("C", 27))
println(people.groupBy { it.age })

如上打印

{
26=[Person(name=A, age=26)], 
27=[Person(name=B, age=27), Person(name=C, age=27)]
}

flatMap、flatten

flatMap根據給定的函數對集合中的每個元素做映射,然后將集合合并,如下打印 [A, 1, B, 2, C, 3]

val strings = listOf("A1", "B2", "C3")
println(strings.flatMap { it.toList() })

如統計圖書館書籍的所有作者,使用Set去除重復元素

data class Book(val title: String, val authors: List<String>)
val books = listOf(Book("A", listOf("Tom")),Book("B", listOf("john")),Book("C", listOf("Tom", "john"))
)
println(books.flatMap { it.authors }.toSet())

flatten用于合并集合,如下打印 [A1, B2, C3]

val strings = listOf("A1", "B2", "C3")
println(listOf(strings).flatten())

序列

序列的好處

map / filter 會創建臨時的中間集合,如下就創建了2個

data class Person(val name: String, val age: Int)
val people = listOf(Person("A", 26), Person("B", 27))
println(people.map(Person::age).filter { it > 26 })

而使用序列可以避免創建

println(people.asSequence().map(Person::age).filter { it > 26 }.toList())

惰性操作及性能

序列的中間操作都是惰性的,如下不會有輸出

listOf(1, 2, 3, 4).asSequence().map { println("map($it)"); it * it }.filter { println("filter($it)");it % 2 == 0 }

只有當末端操作時才會被調用,如toList()

listOf(1, 2, 3, 4).asSequence().map { println("map($it)"); it * it }.filter { println("filter($it)");it % 2 == 0 }.toList()
  • 序列先處理第一個元素,然后再處理第二個元素,故可能有些元素不會被處理,或輪到它們之前就已經返回
  • 若不使用序列,則會先求出map的中間集合,對其調用find
println(listOf(1, 2, 3, 4).asSequence().map { print(" map($it)");it * it }.find { it > 3 })
println(listOf(1, 2, 3, 4).map { print(" map($it)");it * it }.find { it > 3 })

如上都打印4,但序列運行到第二個時已找到滿足條件,后面不會再執行

map(1) map(2)4
map(1) map(2) map(3) map(4)4

序列的順序也會影響性能,第二種方式先filter再map,所執行的變換次數更少

data class Person(val name: String, val age: Int)
val people = listOf(Person("A", 26), Person("AB", 27), Person("ABC", 26), Person("AB", 27))println(people.asSequence().map(Person::name).filter { it.length < 2 }.toList())println(people.asSequence().filter { it.name.length < 2 }.map(Person::name).toList())

創建序列

generateSequence根據前一個元素計算下一個元素

val naturalNumbers = generateSequence(0) { it + 1 }
val numbersTo100 = naturalNumbers.takeWhile { it <= 100 }
println(numbersTo100.sum())

和Java一起使用

函數式接口(SAM接口)

若存在如下Java函數

public class Test {public static void run(int delay, Runnable runnable) {try {Thread.sleep(delay);} catch (InterruptedException e) {e.printStackTrace();}runnable.run();}
}

對于上面接受Runnable的接口,可以傳遞Lambda或創建實例,前者不會創建新的實例,后者每次都會創建

Test.run(1000, Runnable { println("Kotlin") })
Test.run(1000, { println("Kotlin") })
Test.run(1000) { println("Kotlin") }    //最優Test.run(1000, object : Runnable {override fun run() {println("Kotlin")}
})

若Lambda捕捉到了變量,每次調用會創建一個新對象,存儲被捕捉變量的值

  • 若捕捉了變量,則Lambda會被編譯成一個匿名類,否則編譯成單例
  • 若將Lambda傳遞給inline函數,則不會創建任何匿名類
fun handleRun(msg: String) {Test.run(1000) { println(msg) }
}

SAM構造方法

大多數情況下,Lambda到函數式接口實例的轉換都是自動的,但有時候也需要顯示轉換,即使用SAM構造方法,其名稱和函數式接口一樣,接收一個用于函數式接口的Lambda,并返回這個函數式接口的實例

val runnable = Runnable { println("Kotlin") }   //SAMrunnable.run()

如下使用SAM構造方法簡化監聽事件

val listener = View.OnClickListener { view ->val text = when (view.id) {1 -> "1"else -> "-1"}println(text)
}

帶接收者的Lambda

with

fun alphabet(): String {val result = StringBuilder()for (letter in 'A'..'Z') {result.append(letter)}result.append("\nover")return result.toString()
}

上面代碼多次重復result這個名稱,使用with可以簡化,內部可用this調用方法或省略

  • with接收兩個參數,下面例子參數為StringBuilder和Lambda,但把Lambda放在外面
  • with把第一個參數轉換成第二個參數Lambda的接收者
fun alphabet(): String {val result = StringBuilder()return with(result) {for (letter in 'A'..'Z') {this.append(letter)}append("\nover")toString()}
}

還可以進一步優化

fun alphabet() = with(StringBuilder()) {for (letter in 'A'..'Z') {this.append(letter)}append("\nover")toString()
}

apply

with返回的是接收者對象,而不是執行Lambda的結果,而使用apply()會返回接收者對象,可以對任意對象上使用創建對象實例,還能代替Java的Builder

fun alphabet() = StringBuilder().apply {for (letter in 'A'..'Z') {this.append(letter)}append("\nover")
}.toString()

使用庫函數buildString還可以簡化上述操作

fun alphabet() = buildString {for (letter in 'A'..'Z') {this.append(letter)}append("\nover")
}

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/news/166238.shtml
繁體地址,請注明出處:http://hk.pswp.cn/news/166238.shtml
英文地址,請注明出處:http://en.pswp.cn/news/166238.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

EXIT外部中斷 HAL庫+cubeMX

一.cubeMX外部中斷配置 1.系統內核 2.中斷管理 3.選擇搶占優先級和響應優先級&#xff0c;共有5個等級&#xff0c;在這里就使用庫函數編寫代碼時最常用的2位搶占優先級2位響應優先級。 4.勾選使能選項&#xff0c;后面的兩個零&#xff0c;第一個代表搶占優先級的等級&#xf…

用于計算機屏幕安全攝像頭系統:Screen Anytime Crack

Screen Anytime 是一款軟件&#xff0c;旨在自動將整個用戶會話或 PC/服務器/VM/Kiosk 的 /RDP/Citrix/RemoteApp 會話的屏幕活動記錄到視頻日志文件中&#xff0c;以用于記錄、審核和監控目的。通過重播其高度壓縮的視頻&#xff0c;您可以輕松回顧單臺計算機或一組服務器/PC …

基于C#實現并查集

一、場景 有時候我們會遇到這樣的場景&#xff0c;比如:M{1,4,6,8},N{2,4,5,7}&#xff0c;我的需求就是判斷{1,2}是否屬于同一個集合&#xff0c;當然實現方法有很多&#xff0c;一般情況下&#xff0c;普通青年會做出 O(MN)的復雜度&#xff0c;那么有沒有更輕量級的復雜度呢…

Vatee萬騰科技的獨特力量:Vatee數字時代創新的新視野

在數字化時代的浪潮中&#xff0c;Vatee萬騰科技以其獨特而強大的創新力量&#xff0c;為整個行業描繪了一幅嶄新的視野。這不僅是一場科技創新的冒險&#xff0c;更是對未來數字時代發展方向的領先探索。 Vatee萬騰將創新視為數字時代發展的引擎&#xff0c;成為推動行業向前的…

ubuntu 安裝python3.13

列出 /usr/bin/ 目錄下所有以 python 開頭的文件和目錄 ls /usr/bin/python* 添加Python軟件源。您可以通過以下命令將Python的軟件源添加到您的系統中 sudo add-apt-repository ppa:deadsnakes/ppa 然后運行以下命令以更新軟件包列表&#xff1a; sudo apt-get update 安…

vue每個階段的生命周期做了什么

Vue 實例的生命周期可以分為創建階段、掛載階段、更新階段和銷毀階段。下面是每個階段具體干了什么的說明和對應的代碼示例&#xff1a; 創建階段 beforeCreate&#xff1a; 此階段在實例初始化之后&#xff0c;數據觀測 (data observer) 和 event/watcher 事件配置之前被調用…

Spring AOP 底層原理

Spring AOP 底層原理 aop 底層是采用動態代理機制實現的&#xff1a;接口實現類 &#xff08;1&#xff09;如果要代理的對象&#xff0c;實現了某個接口&#xff0c;那么 Spring AOP 會使用 JDK Proxy&#xff0c;去創建代理對象。 &#xff08;2&#xff09;沒有實現接口的對…

下一代ETL工具:微服務架構的全新數據集成平臺

當前對于大型企業來說數據的整合和加工變得越來越重要。隨著業務需求的不斷增長&#xff0c;企業數據量越來越大&#xff0c;數據管道越來越多&#xff0c;現有的ETL&#xff08;抽取、轉換、加載&#xff09;工具已不再滿足實時、高性能和微服務架構等現代化需求。因此&#x…

基于C#實現Prim算法

圖論在數據結構中是非常有趣而復雜的&#xff0c;作為 Web 碼農的我&#xff0c;在實際開發中一直沒有找到它的使用場景&#xff0c;不像樹那樣的頻繁使用&#xff0c;不過還是準備仔細的把圖論全部過一遍。 一、最小生成樹 圖中有一個好玩的東西叫做生成樹&#xff0c;就是用…

前端項目搭建Webpack的配置

本人這次是在搭建一個Typescript項目時候配置的Webpack。但是Typescript的項目本人看來和往常的Web(Vue)項目類似點很多的。那么我們就可以通過對該Typescript項目的略微調整即可挪到Web項目中....... 首先說明一下為什么要依賴WebPack來搭建項目&#xff1f;&#xff1f;&…

ES 萬條以外分頁檢索功能實現及注意事項

背景 以 ES 存儲日志&#xff0c;且需要對日志進行分頁檢索&#xff0c;當數據量過大時&#xff0c;就面臨 ES 萬條以外的數據檢索問題&#xff0c;如何利用滾動檢索實現這個需求呢&#xff1f;本文介紹 ES 分頁檢索萬條以外的數據實現方法及注意事項。 需求分析 用 ES 存儲數…

Redis 反序列化失敗

文章目錄 問題原序列化配置修改配置解決方法 問題 com.fasterxml.jackson.databind.exc.MismatchedInputException: Cannot construct instance of org.springframework.security.core.authority.SimpleGrantedAuthority (although at least one Creator exists): cannot deser…

css圖片縮放屬性object-fit說明

object-fit 屬性可以設置以下值&#xff1a; 屬性值說明例子fill填充容器&#xff0c;可能會改變圖片的比例。object-fit: fill;contain保持圖片的原始比例&#xff0c;確保圖片完全包含在容器內。object-fit: contain;cover保持圖片的原始比例&#xff0c;確保圖片覆蓋整個容…

性能優化中使用Profiler進行頁面卡頓的排查及解決方式

文章目錄 一、前言二、頁面卡頓的排查方式1、耗時操作的監控2、頁面卡頓的監控 三、參考鏈接 一、前言 程序的優化在做過線上bug處理&#xff0c;布局層級優化&#xff0c;項目依賴庫版本更新&#xff0c;重復庫合并&#xff0c;刪除未使用的資源&#xff0c;刪除冗余的庫&…

機器學習【01】相關環境的安裝

學習實例 參考資料&#xff1a;聯邦學習實戰{楊強}https://book.douban.com/subject/35436587/ 項目地址&#xff1a;https://github.com/FederatedAI/Practicing-Federated-Learning/tree/main/chapter03_Python_image_classification 一、環境準備 GPU安裝CUDA、cuDNN pytho…

ComboGrid中快捷鍵Enter使用

為了實現當前元素&#xff0c;回車時有值跳轉到下一個元素&#xff0c;無值則查詢。 定義元素時使用快捷鍵 $.fn.combogrid.defaults.keyHandler.up.call(this);調用combogrid默認的快捷鍵 $(#cs).combogrid({width: 360,placeholder: 測試...,panelWidth: 1000,qParams: {pJ…

letcode::數組中的第k個最大元素

數組中的第k個最大元素 給定整數數組 nums 和整數 k&#xff0c;請返回數組中第 k 個最大的元素。 請注意&#xff0c;你需要找的是數組排序后的第 k 個最大的元素&#xff0c;而不是第 k 個不同的元素。 你必須設計并實現時間復雜度為 O(n) 的算法解決此問題。 示例 1: 輸入: …

PHP 語法||PHP 變量

PHP 腳本在服務器上執行&#xff0c;然后將純 HTML 結果發送回瀏覽器。 基本的 PHP 語法 PHP 腳本可以放在文檔中的任何位置。 PHP 腳本以 <?php 開始&#xff0c;以 ?> 結束&#xff1a; <?php // PHP 代碼 ?> 值得一提的是&#xff0c;通過設定php.ini的相…

nvm-切換node版本工具安裝-方便好用

去官網下載&#xff1a; https://github.com/coreybutler/nvm-windows#installation--upgrades 網站進去后點擊下載&#xff0c;點擊那個exe文件就下載本地&#xff0c;然后雙擊安裝 安裝nvm 就直接按照窗口提示的下一步就行&#xff0c;如果改了某些地方會不成功&#xf…