集合
List、set 、map 分兩類,只讀和可變
List創(chuàng)建與元素獲取
getOrElse是一個(gè)安全索引取值函數(shù),他需要兩個(gè)參數(shù),第一個(gè)是索引值,第二個(gè)是能提供默認(rèn)值的lambda表達(dá)式,如果索引值不存在的話,可用來代替異常。
getOrNull是Kotlin提供的另一個(gè)安全索引取值函數(shù),他返回null結(jié)果,而不是拋出異常。
fun main(){
val list :List<String> = listOf("jason","jack","jacky")
//list[4]
println(list.getOrElse(4){"Unknown"})//Unknown
println(list.getOrNull(4))//null
println(list.getOrNull(4)?:"Unknow")
}
可變List集合
在Kotlin中,支持內(nèi)容修改的列表叫可變列表,要?jiǎng)?chuàng)建可變列表,可以使用mutableListOf函數(shù)。list還支持使用toList和toMutableList函數(shù)動(dòng)態(tài)實(shí)現(xiàn)只讀列表和可變列表的相互轉(zhuǎn)換。
fun main(){
val mutableList = mutableListOf("jason","jack","jacky")
mutableList.add("jimmy")
mutableList.remove("jack")
println(mutableList)
listOf("jason","jack","jacky").toMutableList()
mutableListOf("jason","jack","jacky").toList()
}
mutator函數(shù)
能夠修改可變列表的函數(shù)有個(gè)統(tǒng)一的名字:mutator函數(shù)
添加元素運(yùn)算符與刪除元素運(yùn)算符
基于lambda表達(dá)式指定的條件刪除元素
fun main(){
val mutableList =mutableListOf("jason","jack","jacky")
mutableList += "jimmy"
println(mutableList)
mutableList -="jason"
println(mutableList)
//removeIf
mutableList.removeIf{it.contains("jack")}
println(mutableList)
}
List集合遍歷
for in 遍歷
forEach 遍歷
forEachIndexed遍歷時(shí)要獲取索引
fun mian(){
val list = listOf("jason","jack","jacky")
for(s :String in list){
println(s)
}
list.forEach{
println(it)
}
list.forEachIndexed {index,item->
println("$index, $item")
}
}
解構(gòu)語(yǔ)法過濾元素
通過_符號(hào)過濾不要的元素
fun main(){
val list = listOf("jason","jack","jacky")
val (origin,_,proxy) = list
}
set集合
set創(chuàng)建與元素獲取
list可以有重復(fù)的;set不可以
通過setOf創(chuàng)建set集合,使用elementAt函數(shù)讀取集合中的元素
fun main(){
val set = setOf("Kotlin","Java","Scala")
//set[3]// 沒有這種寫法
set.elementAt(2)
}
可變set集合
通過mutableSetOf創(chuàng)建可變的set集合
fun main(){
val mutableSet = mutableSetOf("Kotlin","Java","Scala")
mutableSet +="Groovy"
}
集合轉(zhuǎn)換與快捷函數(shù)
把list轉(zhuǎn)換成set,去掉重復(fù)元素
快捷函數(shù)
fun main(){
val list = listOf("jason","jack","jacky","jacky" )
.toSet()
.toList()
println(list)
println(listOf("jason","jack","jacky","jacky").distinct())
}
數(shù)組類型
Kotlin提供各種Array,雖然是引用類型,但是可以編譯成java基本數(shù)據(jù)類型

fun main(){
val intArray = intArrayOf(10,20,30)
listOf(10,20,30).toIntArray()
}
map集合
map的創(chuàng)建
to看上去像關(guān)鍵字,實(shí)際上,他是個(gè)省略了點(diǎn)號(hào)和參數(shù)的特殊函數(shù),to函數(shù)將它左邊和右邊的值轉(zhuǎn)化成一對(duì)Pair
fun main(){
val map = mapOf("Jack" to 20,"Jason" to 18,"Jacky" to 30)
println(map)
mapOf(Pair("Jack",20),Pair("Jason",18))
}
獲取map的值
[]取值運(yùn)算符,讀取鍵對(duì)應(yīng)的值,如果鍵不存在就返回null
getValue,讀取鍵對(duì)應(yīng)的值,如果鍵不存在就拋出異常
getOrElse,讀取鍵對(duì)應(yīng)的值,或者使用匿名函數(shù)返回默認(rèn)值
getOrDefault,讀取鍵對(duì)應(yīng)的值,或者返回默認(rèn)值
fun main(){
val map = mapOf("Jack" to 20,"Jason" to 18,"Jacky" to 30)
println(map["Jack"])//30
println(map.getValue("Jack"))//30
println(map.getOrElse("Rose"){"unknow"})//unknow
println(map.getOrDefault("Rose"),0)//0
}
遍歷map
forEach
fun main(){
val map = mapOf("Jack" to 20,"Jason" to 18,"Jacky" to 30)
map.forEach{ it :Map.Entry<String,Int>
println("${it.key} , ${it.value}")
}
map.forEach{ (key:String, value:Int) ->
println("${key} , ${value}")
}
}
可變map集合
通過mutableMapOf創(chuàng)建可變map
getOrPut鍵值不存在,就添加并返回結(jié)果,否則就返回已有鍵對(duì)應(yīng)的值
fun main(){
val mutableMap = mutableMapOf("Jack" to 20,"Jason" to 18,"Jacky" to 30)
mutableMap +="Jimmy" to 30
mutableMap.put("Jimmy",31)//覆蓋了
println(mutableMap.getOrPut("Jimmy"){18})
mutableMap.getOrPut("Rose"){18}
println(mutableMap )
}
類
定義類與field關(guān)鍵字
針對(duì)你定義的每一個(gè)屬性,kotlin都會(huì)產(chǎn)生一個(gè)field,一個(gè)get,一個(gè)setter,field用來存儲(chǔ)屬性數(shù)據(jù),你不能直接定義field,kotlin會(huì)封裝field,保護(hù)她里面的數(shù)據(jù),質(zhì)保路給getter和setter使用。屬性的getter方法決定你如何讀取屬性值,每個(gè)屬性都有g(shù)etter方法,setter方法決定你如何給屬性賦值,所以只有可變屬性才會(huì)有setter方法,盡管kotllin會(huì)自動(dòng)提供默認(rèn)的getter和setter方法,但在需要控制如何讀寫屬性數(shù)據(jù)時(shí),你也可以自定義他們。
class player{
var name = "jack"
get() = field.capitalize()
set(value){
field = value.trim()
}
}
計(jì)算屬性與防范競(jìng)態(tài)條件
計(jì)算屬性是通過一個(gè)覆蓋的get或set運(yùn)算符來定義的,這時(shí)field就不需要了
class Player{
val rolledValue
get()=(1..6).shuffled().first()
}
如果一個(gè)類的屬性即可空又可變,那么引用他之前你必須保證他非空,一個(gè)辦法是用also標(biāo)準(zhǔn)函數(shù)
class Player{
var words:String ?="hello"
fun say(){
words?.also{it:String
println("hello ${it.toUpperCase()}")
}
}
}
主構(gòu)造函數(shù)
我們?cè)赑layer類的定義頭中定義一個(gè)柱構(gòu)造函數(shù),使用臨時(shí)變量為player的各個(gè)屬性提供初始值,在kotlin中,為便于識(shí)別,臨時(shí)變量(包括僅僅引用一次的參數(shù)),通常都會(huì)以下劃線開頭的名字命名。
class Player(
_name:String,
_age:Int,
_isNormal:Boolean
){
var name = _name
get() = field.capotalize()
set(value){
field = value.trim()
}
var age = _age
var isNormal = _isNormal
}
fun main(){
var player = Player("jack",20,true)
}
在柱構(gòu)造函數(shù)里定義屬性
kotlin允許你不使用臨時(shí)變量賦值,而是直接用一個(gè)定義同時(shí)指定參數(shù)和類屬性,通常,我們更喜歡用這種方式定義類屬性,因?yàn)槟軠p少重復(fù)代碼
class Player2(
_name:String,
var age:Int,
val isNormal:Boolean
){
var name = _name
get() = field.capotalize()
set(value){
field = value.trim()
}
}
次構(gòu)造函數(shù)
可以定義多個(gè)次構(gòu)造函數(shù)來配置不同參數(shù)組合
class Player2(
_name:String,
var age:Int,
val isNormal:Boolean
){
var name = _name
get() = field.capotalize()
set(value){
field = value.trim()
}
constructor(_name:String):this(_name,
age=100
isNormal = false){
//...
}
}
默認(rèn)參數(shù)
定義構(gòu)造函數(shù)時(shí)可以指定默認(rèn)值。
class Player3(
_name:String,
var age:Int = 20,
val isNormal:Boolean
){
var name = _name
get() = field.capotalize()
set(value){
field = value.trim()
}
}
初始化塊
初始化塊可以設(shè)置變量或值,以及執(zhí)行有效性檢查,如檢查傳給某構(gòu)造函數(shù)的值是否有效,初始化塊代碼會(huì)在構(gòu)造類實(shí)例時(shí)執(zhí)行。
init{
require(age>0){"age must be positive"}
require(name.isNotBlank()){"must have a name"}
}
初始化順序
主構(gòu)造函數(shù)里聲明的屬性
類級(jí)別的屬性賦值
init初始化塊里的屬性賦值和函數(shù)調(diào)用
次構(gòu)造函數(shù)里的屬性賦值和函數(shù)調(diào)用
class Student(
_name:String,
val age:Int
){
var name = _name
var score = 10
private val hobby ="music"
val subject :String
init {
println("initializing student")
subject = "math"
}
constructor(_name :String):this(_name,10){
score = 20
}
}
fun main(){
Student("Jack")
}

延遲初始化lateinit
使用lateinit關(guān)鍵字相當(dāng)于做了一個(gè)約定:在用它之前負(fù)責(zé)初始化
只要無法確認(rèn)lateinit變量是否完成初始化,可以執(zhí)行inInitialized檢查
class Player4{
laterinit var equipment:String
fun ready(){
equipment = "sharp knife"
}
fun battle(){
if(::equipment.isInitialized)println(equipment)//沒有初始化就不執(zhí)行了
}
}
fun main(){
val p = Player4()
p.ready()//如果忘了就異常了
p.battle()
}
惰性初始化by lazy
暫時(shí)不初始化某個(gè)變量,直到首次使用它
class Player5(_name :String){
var name = _name
val config by lazy{loadConfig()}
private fun loadConfig():String{
println("loading...")
return "xxx"
}
}
fun main(){
val p = Player5("Jack")
Thread.sleep(3000)
println(p.config)
}
初始化陷阱1
在初始化塊時(shí),順序非常重要,必須保證塊中所有屬性已完成初始化。
class Player6(){
init{
val bloodBonus = blood.times(4)//blood 報(bào)錯(cuò)
}
val blood = 100
}
雖然 類級(jí)別屬性 比初始化塊的 先初始化,但還是要放在前面。順序不能反了,從上到下。
class Player6(){
val blood = 100//要在前面
init{
val bloodBonus = blood.times(4)
}
}
初始化陷阱2
下面這個(gè)便已沒有問題,因?yàn)榫幾g器看到name已經(jīng)在init里初始化了,但是代碼一運(yùn)行,就會(huì)拋出空指針異常,因?yàn)閚ame屬性還沒賦值,firstLetter函數(shù)就用了他。
class Player7(){
val name :String
private fun firstLetter() = name[0]
init {
println(firstLetter())
name = "Jack"http://和上面一行換一下就可以了
}
}
初始化陷阱3
因?yàn)榫幾g器看到所有屬性都初始化了,所以編譯沒有問題,但運(yùn)行確是null。
因?yàn)樵谟胕nitPlayerName函數(shù)初始化playerName時(shí),name屬性還沒有完成初始化
class Player8(_name :String){
val playerName:String = initPlayerName()//這時(shí)拿到的是null
val name:String = _name//把這行放到第一行即可
private fun initPlayerName() = name
}
fun main(){
println(Player8("Jack").playerName)
}