Kotlin 中的集合(List中沒有了addAll())

風吹過wu發表於2019-01-16

為什麼講這個呢,集合還需要再老生常談嗎?,作為一個從java轉向kotlin的人來說,集合還不是手到擒來。這裡主要講講二者之間使用的區別,避免採坑。下面我們從實際案例入手:

想必大多數Android 開發者都有遇到過分頁載入列表的需求吧,比如我們會寫一個新增資料的程式碼

private List<MessageItem> list = null;

public MessageAdapter(List<MessageItem> list) {
    this.list = list;
}

public void appendData(List<MessageItem> list) {//必須是追加
    this.list.addAll(list);
    notifyDataSetChanged();
}

複製程式碼

使用java 編寫 it work fine

但是使用kotlin編寫的時候,按照我們以前的習慣我們往往會這麼寫

class MessageAdapter(var list: List<MessageItem>) {
    
    fun appendData(list: List<MessageItem>) {
        this.list.addAll(list)
        notifyDataSetChanged()
    }
}
複製程式碼

不好意思,直接報錯,說沒有這個方法,點選檢視原始碼如下

/**
 * A generic ordered collection of elements. Methods in this interface support only read-only access to the list;
 * read/write access is supported through the [MutableList] interface.
 * @param E the type of elements contained in the list. The list is covariant on its element type.
 */
public interface List<out E> : Collection<E> {
    // Query Operations
    override val size: Int

    override fun isEmpty(): Boolean
    override fun contains(element: @UnsafeVariance E): Boolean
    override fun iterator(): Iterator<E>

    // Bulk Operations
    override fun containsAll(elements: Collection<@UnsafeVariance E>): Boolean

    // Positional Access Operations
    /**
     * Returns the element at the specified index in the list.
     */
    public operator fun get(index: Int): E

    // Search Operations
    /**
     * Returns the index of the first occurrence of the specified element in the list, or -1 if the specified
     * element is not contained in the list.
     */
    public fun indexOf(element: @UnsafeVariance E): Int

    /**
     * Returns the index of the last occurrence of the specified element in the list, or -1 if the specified
     * element is not contained in the list.
     */
    public fun lastIndexOf(element: @UnsafeVariance E): Int

    // List Iterators
    /**
     * Returns a list iterator over the elements in this list (in proper sequence).
     */
    public fun listIterator(): ListIterator<E>

    /**
     * Returns a list iterator over the elements in this list (in proper sequence), starting at the specified [index].
     */
    public fun listIterator(index: Int): ListIterator<E>

    // View
    /**
     * Returns a view of the portion of this list between the specified [fromIndex] (inclusive) and [toIndex] (exclusive).
     * The returned list is backed by this list, so non-structural changes in the returned list are reflected in this list, and vice-versa.
     *
     * Structural changes in the base list make the behavior of the view undefined.
     */
    public fun subList(fromIndex: Int, toIndex: Int): List<E>
}
複製程式碼

確實沒有addAll,同樣也沒有add的方法。通過看註釋,明白了,kotlin中的List是隻支援讀操作,是immutable,不支援寫操作,如果想支援寫操作,請使用MutableList,我們常用的ArrayList就是實現了MutableList 介面。

/**
 * A generic ordered collection of elements that supports adding and removing elements.
 * @param E the type of elements contained in the list. The mutable list is invariant on its element type.
 */
public interface MutableList<E> : List<E>, MutableCollection<E> {
    // Modification Operations
    /**
     * Adds the specified element to the end of this list.
     *
     * @return `true` because the list is always modified as the result of this operation.
     */
    override fun add(element: E): Boolean

    override fun remove(element: E): Boolean

    // Bulk Modification Operations
    /**
     * Adds all of the elements of the specified collection to the end of this list.
     *
     * The elements are appended in the order they appear in the [elements] collection.
     *
     * @return `true` if the list was changed as the result of the operation.
     */
    override fun addAll(elements: Collection<E>): Boolean

    /**
     * Inserts all of the elements of the specified collection [elements] into this list at the specified [index].
     *
     * @return `true` if the list was changed as the result of the operation.
     */
    public fun addAll(index: Int, elements: Collection<E>): Boolean

    override fun removeAll(elements: Collection<E>): Boolean
    override fun retainAll(elements: Collection<E>): Boolean
    override fun clear(): Unit

    // Positional Access Operations
    /**
     * Replaces the element at the specified position in this list with the specified element.
     *
     * @return the element previously at the specified position.
     */
    public operator fun set(index: Int, element: E): E

    /**
     * Inserts an element into the list at the specified [index].
     */
    public fun add(index: Int, element: E): Unit

    /**
     * Removes an element at the specified [index] from the list.
     *
     * @return the element that has been removed.
     */
    public fun removeAt(index: Int): E

    // List Iterators
    override fun listIterator(): MutableListIterator<E>

    override fun listIterator(index: Int): MutableListIterator<E>

    // View
    override fun subList(fromIndex: Int, toIndex: Int): MutableList<E>
}

複製程式碼

一看,果然支援各種add還有remove方法。上面的問題應該比較清晰了吧。現在我們再系統的瞭解一下Kotlin中的集合

集合:List、Set、Map

與大多數語言不同,Kotlin 區分可變集合與不可變集合(lists、sets、maps 等)。精確控制什麼時候集合可編輯有助於消除 bug 以及設計良好的 API。

預先了解一個可變集合的只讀 檢視 與一個真正的不可變集合之間的區別是很重要的。它們都容易建立,但型別系統不能表達它們的差別,所以由你來跟蹤(是否相關)。

Kotlin 的 List 型別是一個提供只讀操作如 size、get等的介面。與 Java 類似,它繼承自 Collection 進而繼承自 Iterable。改變 list 的方法是由 MutableList 加入的。這一模式同樣適用於 Set/MutableSet 及 Map<K, out V>/MutableMap<K, V>。

我們可以看下 list 及 set 型別的基本用法:

val numbers: MutableList<Int> = mutableListOf(1, 2, 3)
val readOnlyView: List<Int> = numbers
println(numbers)        // 輸出 "[1, 2, 3]"
numbers.add(4)
println(readOnlyView)   // 輸出 "[1, 2, 3, 4]"
readOnlyView.clear()    // -> 不能編譯
​
val strings = hashSetOf("a", "b", "c", "c")
assert(strings.size == 3)
複製程式碼

Kotlin 沒有專門的語法結構建立 list 或 set。 要用標準庫的方法,如 listOf()、 mutableListOf()、 setOf()、 mutableSetOf()。 在非效能關鍵程式碼中建立 map 可以用一個簡單的慣用法來完成:mapOf(a to b, c to d)。

注意上面的 readOnlyView 變數(譯者注:與對應可變集合變數 numbers)指向相同的底層 list 並會隨之改變。 如果一個 list 只存在只讀引用,我們可以考慮該集合完全不可變。建立一個這樣的集合的一個簡單方式如下:

val items = listOf(1, 2, 3) 目前 listOf 方法是使用 singletonList 實現的,但是未來可以利用它們知道自己不能變的事實,返回更節約記憶體的完全不可變的集合型別。

注意這些型別是協變的。這意味著,你可以把一個 List 賦值給 List 假定 Rectangle 繼承自 Shape(集合型別與元素型別具有相同的繼承關係)。對於可變集合型別這是不允許的,因為這將導致執行時故障:你可能向 List 中新增一個 Circle,而在程式的其他地方建立了一個其中含有 Circle 的 List`。

有時你想給呼叫者返回一個集合在某個特定時間的一個快照, 一個保證不會變的:

class Controller {
    private val _items = mutableListOf<String>()
    val items: List<String> get() = _items.toList()
}
複製程式碼

這個 toList 擴充套件方法只是複製列表項,因此返回的 list 保證永遠不會改變。

List 與 set 有很多有用的擴充套件方法值得熟悉:

val items = listOf(1, 2, 3, 4)
items.first() == 1
items.last() == 4
items.filter { it % 2 == 0 }   // 返回 [2, 4]
​
val rwList = mutableListOf(1, 2, 3)
rwList.requireNoNulls()        // 返回 [1, 2, 3]
if (rwList.none { it > 6 }) println("No items above 6")  // 輸出“No items above 6”
val item = rwList.firstOrNull()
複製程式碼

…… 以及所有你所期望的實用工具,例如 sort、zip、fold、reduce 等等。

非常值得注意的是,對只讀集合返回修改後的集合的操作(例如 +、 filter、 drop 等)並不會以原子方式建立其結果,因此如果沒有合適的同步機制,在不同的執行緒中使用其結果是不安全的。

Map 遵循同樣模式。它們可以容易地例項化與訪問,像這樣:


val readWriteMap = hashMapOf("foo" to 1, "bar" to 2)
println(readWriteMap["foo"])  // 輸出“1”
val snapshot: Map<String, Int> = HashMap(readWriteMap)
複製程式碼

參考

www.kotlincn.net/docs/refere…

相關文章