1. 程式人生 > >Kotlin入門(16)容器的遍歷方式

Kotlin入門(16)容器的遍歷方式

pair 覆蓋 -i temp tor div 添加元素 style 進行

Kotlin號稱全面兼容Java,於是乎Java的容器類仍可在Kotlin中正常使用,包括大家熟悉的隊列ArrayList、映射HashMap等等。不過Kotlin作為一門全新的語言,肯定還是要有自己的容器類,不然哪天Java跟Kotlin劃清界限,那麻煩就大了。與Java類似,Kotlin也擁有三類基本的容器,分別是集合Set、隊列List、映射Map,然後每類容器又分作只讀與可變兩種類型,這是為了判斷該容器能否進行增刪改等變更操作。Kotlin對修改操作很慎重,比如變量用val前綴表示不可修改,用var前綴表示允許修改;類默認是不允許繼承的,只有添加open前綴才允許該類被繼承;至於容器默認為只讀容器,如果需要進行修改則需加上Mutable形成新的容器,比如MutableSet表示可變集合,MutableList表示可變隊列,MutableMap表示可變映射。


既然Set/List/Map都屬於容器,那麽必定擁有相同的基本容器方法,具體說明如下:
isEmpty : 判斷該容器是否為空。
isNotEmpty : 判斷該容器是否非空。
clear : 清空該容器。
contains : 判斷該容器是否包含指定元素。
iterator : 獲取該容器的叠代器。
count : 獲取該容器包含的元素個數,也可通過size屬性獲得元素數量。
初始化賦值 : Kotlin允許在聲明容器變量之時進行初始賦值,這點很方便比Java先進,當然不同容器的初始化方法有所區別,具體的對應關系見下表:
只讀集合Set setOf
可變集合 mutableSetOf
只讀隊列List listOf
可變隊列MutableList mutableListOf
只讀映射Map mapOf
可變映射MutableMap mutableMapOf
以上是Kotlin容器的基本方法,更具體的增刪改查等用法則有所不同,下面分別介紹這三類六種容器的詳細用法。

只讀集合Set/可變集合MutableSet
集合是一種簡單的容器,它具有以下特性:
1、容器內部的元素不按順序排列,因此無法按照下標進行訪問;
2、容器內部的元素存在唯一性,通過哈希值校驗是否存在相同的元素,如果存在則覆蓋之;
因為Set是只讀集合,初始化賦值後便不可更改,所以元素變更的方法只適用於可變集合MutableSet,但MutableSet的變更操作尚有以下限制:
1、MutableSet的add方法僅僅往集合中添加元素,由於集合是無序的,因此不知道添加的具體位置;
2、MutableSet沒有修改元素值的方法,一個元素一旦被添加,就不可被修改;
3、MutableSet的remove方法用於刪除指定對象,但無法刪除某個位置的元素,這是因為集合內的元素不是按順序排列的;

對於集合的遍歷操作,Kotlin提供了好幾種方式,有熟悉的for循環,有叠代器循環,還有新面孔forEach循環,三種循環遍歷的用法說明如下:


1、for-in循環
與Java類似,通過for語句加上in條件,即可輕輕松松依次取出集合中的所有元素。下面是運用了for-in循環的代碼例子:

    btn_set_for.setOnClickListener {
        var desc = ""
        //使用for-in語句循環取出集合中的每條記錄
        for (item in goodsMutSet) {
            desc = "${desc}名稱:${item.name},價格:${item.price}\n"
        }
        tv_set_result.text = "手機暢銷榜包含以下${goodsMutSet.size}款手機:\n$desc"
    }

  

2、叠代器循環
叠代器與指針的概念有點接近,它自身並非具體的元素,而是指向元素的存放地址,所以叠代器循環其實是遍歷所有元素的地址。叠代器通過hasNext方法判斷是否還存在下一個節點,如果不存在下一節點則表示已經遍歷完畢;它通過next方法獲得下一個節點的元素,同時叠代器自身改為指向該元素的地址。下面是運用了叠代器循環的代碼例子:

    btn_set_iterator.setOnClickListener {
        var desc = ""
        val iterator = goodsMutSet.iterator()
        //如果叠代器還存在下一個節點,則繼續取出下一個節點的記錄
        while (iterator.hasNext()) {
            val item = iterator.next()
            desc = "${desc}名稱:${item.name},價格:${item.price}\n"
        }
        tv_set_result.text = "手機暢銷榜包含以下${goodsMutSet.size}款手機:\n$desc"
    }

  

3、forEach循環
不管是for-in循環還是叠代器循環,其實都脫胎於Java已有的容器遍歷操作,代碼書寫上不夠精煉。為了將代碼精簡做到極致,Kotlin給容器創造了forEach方法,明確指定該方法就是要依次遍歷容器。forEach方法在編碼時采用匿名函數的形式,內部使用it代表每個元素的對象,下面是運用了forEach循環的代碼例子:

    btn_set_foreach.setOnClickListener {
        var desc = ""
        //forEach內部使用it指代每條記錄
        goodsMutSet.forEach { desc = "${desc}名稱:${it.name},價格:${it.price}\n" }
        tv_set_result.text = "手機暢銷榜包含以下${goodsMutSet.size}款手機:\n$desc"
    }

  

結合以上有關Set的用法說明,我們發現集合在實戰中存在諸多不足,主要包括以下幾點:
1、集合不允許修改內部元素的值;
2、集合無法刪除指定位置的元素;
3、不能通過下標獲取指定位置的元素;
鑒於集合的以上缺點難以克服,故而實際開發基本用不到集合,大多數場合用的是它的兩個兄弟——隊列和映射。

只讀隊列List/可變隊列MutableList
隊列是一種元素之間按照順序排列的容器,它與集合的最大區別,便是多了個次序管理。不要小看這個有序性,正因為隊列建立了秩序規則,所以它比集合多提供了如下功能(註意凡是涉及到增刪改的,都必須由MutableList來完成):
1、隊列的get方法能夠獲取指定位置的元素,也可直接通過下標獲得該位置的元素。
2、MutableList的add方法每次都是把元素添加到隊列末尾,也可指定添加的位置;
3、MutableList的set方法允許替換或者修改指定位置的元素;
4、MutableList的removeAt方法允許刪除指定位置的元素;
5、MutableList提供了sort系列方法用於給隊列中的元素重新排序,其中sortBy方法表示按照升序排列,sortByDescending方法表示按照降序排列;下面是個給隊列排序的代碼例子:

    var sortAsc = true
    btn_sort_by.setOnClickListener {
        if (sortAsc) {
            //sortBy表示升序排列,後面跟的是排序條件
            goodsMutList.sortBy { it.price }
        } else {
            //sortByDescending表示降序排列,後面跟的是排序條件
            goodsMutList.sortByDescending { it.price }
        }
        var desc = ""
        for (item in goodsMutList) {
            desc = "${desc}名稱:${item.name},價格:${item.price}\n"
        }
        tv_list_result.text = "手機暢銷榜已按照${if (sortAsc) "升序" else "降序"}重新排列:\n$desc"
        sortAsc = !sortAsc
    }

  


5、隊列除了擁有跟集合一樣的三種遍歷方式(for-in循環、叠代器循環、forEach循環),另外多了一種按元素下標循環遍歷的方式,具體下標遍歷的代碼例子如下所示:

    btn_for_index.setOnClickListener {
        var desc = ""
        //indices是隊列的下標數組。如果隊列大小為10,則下標數組的取值為0到9
        for (i in goodsMutList.indices) {
            val item = goodsMutList[i]
            desc = "${desc}名稱:${item.name},價格:${item.price}\n"
        }
        tv_list_result.text = "手機暢銷榜包含以下${goodsMutList.size}款手機:\n$desc"
    }

  

只讀映射Map/可變映射MutableMap
映射內部保存的是一組鍵值對(Key-Value),也就是說,每個元素都由兩部分構成,第一部分是元素的鍵,相當於元素的名字;第二部分是元素的值,存放著元素的詳細信息。元素的鍵與值是一一對應的關系,相同的鍵名指向的值對象是唯一的,所以映射中每個元素的鍵名各不相同,這個特性使得映射的變更操作與隊列存在以下不同之處(註意增刪操作必須由MutableMap來完成):
1、映射的containsKey方法判斷是否存在指定鍵名的元素,containsValue方法判斷是否存在指定值對象的元素;
2、MutableMap的put方法不單單是添加元素,而是智能的數據存儲;每次調用put方法,映射會先根據鍵名尋找同名元素,如果找不到就添加新元素,如果找得到就用新元素替換舊元素;
3、MutableMap的remove方法,是通過鍵名來刪除元素的;
4、調用mapOf和mutableMapOf方法初始化映射之時,有兩種方式可以表達單個鍵值對元素。其一是采取“鍵名 to 值對象”的形式,其二是采取Pair配對方式形如“Pair(鍵名, 值對象)”,下面是這兩種初始化方式的代碼例子:

//to方式初始化映射
var goodsMap = mapOf("蘋果" to goodsA, "華為" to goodsB, "小米" to goodsC, "歐珀" to goodsD, "步步高" to goodsE, "魅族" to goodsF)
//Pair方式初始化映射
var goodsMutMap = mutableMapOf(Pair("蘋果", goodsA), Pair("華為", goodsB), Pair("小米", goodsC), Pair("歐珀", goodsD), Pair("步步高", goodsE), Pair("魅族", goodsF))

  

映射的遍歷與集合類似,也有for-in循環、叠代器循環、forEach循環三種遍歷手段。但是由於映射的元素是個鍵值對,因此它的循環遍歷方式與集合稍有不同,詳述如下:
1、for-in循環
for-in語句取出來的是映射的鍵值對元素,若要獲取該元素的鍵名,還需訪問元素的key屬性;若要獲取該元素的值對象,還需訪問元素的value屬性。下面是在映射中運用for-in循環的代碼例子:

    btn_map_for.setOnClickListener {
        var desc = ""
        //使用for-in語句循環取出映射中的每條記錄
        for (item in goodsMutMap) {
            //item.key表示該配對的鍵,即廠家名稱;item.value表示該配對的值,即手機信息
            desc = "${desc}廠家:${item.key},名稱:${item.value.name},價格:${item.value.price}\n"
        }
        tv_map_result.text = "手機暢銷榜包含以下${goodsMutMap.size}款手機:\n$desc"
    }

  

2、叠代器循環
映射的叠代器通過next函數得到下一個元素,也需訪問該元素的key屬性獲取鍵名,訪問該元素的value屬性獲取值對象。下面是在映射中運用叠代器循環的代碼例子:

    btn_map_iterator.setOnClickListener {
        var desc = ""
        val iterator = goodsMutMap.entries.iterator()
        //如果叠代器還存在下一個節點,則繼續取出下一個節點的記錄
        while (iterator.hasNext()) {
            val item = iterator.next()
            desc = "${desc}廠家:${item.key},名稱:${item.value.name},價格:${item.value.price}\n"
        }
        tv_map_result.text = "手機暢銷榜包含以下${goodsMutMap.size}款手機:\n$desc"
    }

  

3、forEach循環
映射的forEach方法內部依舊采用匿名函數的形式,同時把元素的key和value作為匿名函數的輸入參數。不過映射的forEach函數需要API24及以上版本支持,開發時註意修改編譯配置。下面是在映射中運用forEach循環的代碼例子:

    btn_map_foreach.setOnClickListener {
        //var desc = ""
        ////映射的forEach函數需要API24及以上版本支持
        ////forEach內部使用key指代每條記錄的鍵,使用value指代每條記錄的值
        //goodsMap.forEach { key, value -> desc = "${desc}廠家:${key},名稱:${value.name},價格:${value.price}\n" }
        //tv_map_result.text = "手機暢銷榜包含以下${goodsMutMap.size}款手機:\n$desc"
        tv_map_result.text = "Map的forEach函數需要API24及以上版本支持"
    }

  

__________________________________________________________________________

打開微信掃一掃下面的二維碼,或者直接搜索公眾號“老歐說安卓”添加關註,更快更方便地閱讀技術幹貨。

技術分享圖片

Kotlin入門(16)容器的遍歷方式