Kotlin集合與陣列詳解
可空性和集合
函式的引數可以為可空的,集合的型別引數也是可以宣告為可空的. 下面來看一個建立一個包含可空值的集合
fun readNumbers(reader : BufferedReader) : List<Int?>{ val result = ArrayList<Int?>() for(line in reader.lineSequence()){ try{ val number = line.toInt() result.add(number) } catch(e : NumberFormatException){ result.add(null) } } return result }
List<Int?>可以持有Int或者null. 另外還要注意下面情況,列表中的單個值是可空的和整個列表是可空的.
另外還有一種情況是,你可能需要宣告一個變數持有可空的列表,並且包含可空的數字。Kotlin中的寫法是List<Int?>?, 有兩個問號。這種情況下使用變數自己的值的時候,以及使用列表中每個元素的值的時候,都需要使用null檢查.
下面一個例子展示使用可空值的集合
fun addValidNumbers(numbers : List<Int?>){ var sumOfValidNumbers = 0 var invalidNumbers = 0 for(number in numbers){ if (number != null){ sumOfValidNumbers += number } else{ invalidNumbers++ } } printlin(“Sum of valid numbers : $sumOfValidNumbers”) printlin(“Invalid numbers : ” $invalidNumbers) }
在使用List中每一個元素之前必須對其進行判空.
遍歷一個包含可空值的集合並過濾掉null是一個非常常見的操作,因此Kotlin提供了一個標準庫函式filterNotNull來完成它. 這裡可以用它來大大簡化前面的例子.
fun addValidNumbers(numbers : List<Int?>) { val validNumbers = numbers.filterNotNull() println(“Sum of valid numbers : ${validNumbers.sum()}”) println(“Invalid numbers : ${numbers.size - validNumbers.size}”) }
只讀集合和可變集合
Kotlin的集合設計和Java不同的另一項重要特質是,它把訪問集合資料的藉口和修改集合資料的介面分開了。這種區別在於最基礎的使用集合的介面之中:kotlin.collections.Collection. 使用這個介面可以遍歷集合中的元素,獲取集合大小,判斷集合中是否包含某個元素,以及執行其他從該集合中讀取資料的操作。但這個介面沒有任何新增或移除元素的方法.
而另外一個kotlin.collections.MutableCollection介面可以修改集合中的資料。它繼承了普通的kotlin.collections.Collection介面,它還提供了方法來新增和移除和清空集合等.
把只讀集合和可變集合分離的好處在於,如果函式接收Collection而不是MutableCollection作為形參,你就知道它只是讀取集合中的資料。如果函式要求你傳遞給它MutableCollection,可以認為它將會修改資料.
下面一個例子展示使用只讀集合與可變集合介面
fun <T> copyElements(source : Collection<T>, target: MutableCollection<T>){ for(item in source){ target.add(item)//向可變的target集合中新增元素 } } >>> val source : Collection<Int> = arrayListOf(3,5,7) >>> val target : MutableCollection<Int> = arrayListOf(1) >>> copyElements(source, target) >>> println(target) [1,3,5,7]
使用集合介面時需要牢記一個關鍵點是隻讀集合不一定是不可變的. 如果你使用的變數擁有一個只讀介面型別,它可能只是同一個集合的眾多引用中的一個。任何其他的引用都可能擁有一個可變介面型別。
一個集合多個引用(包含可變集合引用)的情況需要注意執行緒安全問題,和java一樣同一個集合不允許多執行緒進行修改和讀取.
作為平臺型別的集合
在kotlin中會吧所有java中的型別作為平臺型別.kotlin沒有任何關於平臺型別的可空性資訊,所以編譯器允許Kotlin程式碼將其視為可空或者非空. 同樣,Java中宣告的集合型別的變數也被視為平臺型別.
/*Java*/ interface FileContentProcessor{ void processContents(File path,byte[] binaryContents, List<String> textContents); }
這個介面的Kotlin實現需要做出下面的選擇:
列表將可能是可空的,因為有些檔案是二進位制格式,它們的內容不能被表示成文字.
列表中的元素將會是非空的,因為檔案中每一行都永遠不為null
列表將是隻讀的,因為表示的是檔案的內容,而這些內容不會被改變
下面來看看kotlin中實現的樣子:
class FileIndexer : FileContentProcessor{ override fun processContents(path : File, binaryContents : ByteArray?, textContents: List<String>?) }
另一個使用集合引數的java介面
/Java/ interface DataParser<T>{ void parseData(String input, List<T> output, List<String> errors) }
這種情況下需要做出下面的選擇
List<String>將是非空的,因為呼叫者總是需要接收錯誤訊息
列表中的元素將是可空的, 因為不是每個輸出列表中的條目都有關聯的錯誤資訊.
List<String>將是可變的,因為實現程式碼需要向其中新增元素.
下面是具體實現的樣子:
class PersonParser : DataParser<Person>{ override fun parseData(input : String, output : MutableList<Person>, errors: MutableList<String?>) }
同樣的Java型別List<String>表示成了不同的Kotlin型別:一種是List<String>? (包含字串的可空列表) ,一種是List<String>? (包含空字串的可變列表). 為了做出正確的選擇,你可以根據需求做出不同的選擇.
物件和基本資料型別的陣列
kotlin使用陣列
fun main(args : Array<List>){ for(i in args.indices){ println(“Argument $i is : ${arg[i]}") } }
Kotlin中一個數組是一個帶有型別引數的類,其元素型別被指定為相應的型別引數.
要在Kotlin中建立陣列,有下面這些方法供你選擇:
arrayOf函式建立一個數組,它包含的元素是為該函式的實參
arrayOfNulls建立一個給定大小的陣列,包含的是null元素。當然,它只能用來建立包含元素型別的可空陣列
Array構造方法接受陣列的大小和一個lambda表示式,呼叫lambda表示式來建立每一個數組元素
val letters = Array<String>(26){ i -> (‘a’+i).toString } println(letters.joinToString(“”))
集合轉換成陣列
val strings = listOf(“a”,”b”,”c”) println(“%s/%s/%s”.format(*string.toTypeArray()))
和其他型別一樣,陣列型別的型別引數始終會變成物件型別。如果宣告一個Array<Int>,它將會是一個包含裝箱整型的陣列(它的Java型別將是java.lang.Integer[]).如果你需要建立沒有裝箱的基本資料型別陣列,必須使用一個基本資料型別陣列的特殊類.
為了表示基本型別陣列,Kotlin提供了若干獨立的類,每一種基本資料型別都對應一個。例如,Int型別值的陣列叫做IntArray. Kotlin還提供了ByteArray,CharArray,BooleanArray等給其他型別.所有這些型別都被編譯成普通的Java基本資料型別陣列,比如
int[],byte[],char[]等.這些陣列的值儲存時並沒有裝箱,而是使用了更高效的方式.
要建立基本資料型別的陣列,有如下幾種方法
- 該型別的構造方法接收size引數並返回一個使用對應基本資料型別預設值(通常是0)初始化好的陣列
val fiveZeros = IntArray(5)
- 工廠函式(IntArray的intArrayOf,以及其他陣列型別的函式)接收變長引數的值並建立儲存這些值的陣列
val fiveZeroToo = intArrayof(0,0,0,0,0)
- 另一種構造方法,接收一個大小和一個用來初始化每個元素的lambda.
val squares = IntArray(5){i -> (i+1) * (i+1)} println(squares.joinToString()) 1,4,9,16,25
另外kotlin標準庫支援一套和集合相同的對於陣列的擴充套件函式。(例如filter,map等)也適用於陣列。
最後
在現在這個金三銀四的面試季,我自己在網上也蒐集了很多資料做成了文件和架構視訊資料免費分享給大家【 包括高階UI、效能優化、架構師課程、NDK、Kotlin、混合式開發(ReactNative+Weex)、Flutter等架構技術資料 】,希望能幫助到您面試前的複習且找到一個好的工作,也節省大家在網上搜索資料的時間來學習。
資料獲取方式:加入Android架構交流QQ群聊:513088520 ,進群即領取資料!!!
點選連結加入群聊【Android移動架構總群】:加入群聊
資料大全