# Kotlin 集合概述
[TOC]
Kotlin 標準庫提供了一整套用于管理集合的工具,集合是可變數量(可能為零)的一組條目,各種集合對于解決問題都具有重要意義,并且經常用到。
集合是大多數編程語言的常見概念,因此如果熟悉像 Java 或者 Python 語言的集合,那么可以跳過這一介紹轉到詳細部分。
集合通常包含相同類型的一些(數目也可以為零)對象。集合中的對象稱為**元素或條目**。例如,一個系的所有學生組成一個集合,可以用于計算他們的平均年齡。
以下是 Kotlin 相關的集合類型:
* _List_ 是一個**有序**集合,可通過索引(反映元素位置的整數)訪問元素。元素可以在 list 中出現多次(**可重復**)。列表的一個示例是一句話:**有一組字(元素)、這些字的順序很重要并且字可以重復**。
* _Set_ 是唯一元素的集合。它反映了集合(set)的數學抽象:**一組無重復的對象**。一般來說 set 中元素的順序并不重要(無序)。例如,字母表是字母的集合(set)。
* _Map_(或者*字典*)是一組鍵值對。**鍵是唯一的**,每個鍵都剛好映射到一個值。**值可以重復**。map 對于存儲對象之間的邏輯連接非常有用,例如,員工的 ID 與員工的位置。
**Kotlin 讓你可以獨立于所存儲對象的確切類型來操作集合**。換句話說,將 `String` 添加到 `String` list 中的方式與添加 `Int` 或者用戶自定義類的到相應 list 中的方式相同。因此,**Kotlin 標準庫為創建、填充、管理任何類型的集合提供了泛型的(通用的,雙關)接口、類與函數**。這些集合接口與相關函數位于 kotlin.collections 包中。我們來大致了解下其內容。
## 集合類型
Kotlin 標準庫提供了基本集合類型的實現: set、list 以及 map。
一對接口代表每種集合類型:
* 一個 _只讀_ 接口,提供訪問集合元素的操作。
* 一個 _可變_ 接口,通過寫操作擴展相應的只讀接口:添加、刪除和更新其元素。
請注意,更改可變集合不需要它是以 [`var`](http://www.kotlincn.net/docs/reference/basic-syntax.html#defining-variables) 定義的變量:寫操作修改同一個可變集合對象,因此引用不會改變。但是,*如果嘗試對 `val` **集合**重新賦值,你將收到編譯錯誤*。
```kotlin
fun main() {
//sampleStart
val numbers = mutableListOf("one", "two", "three", "four")
numbers.add("five") // 這是可以的
//numbers = mutableListOf("six", "seven") // 編譯錯誤
//sampleEnd
}
```
只讀集合,類型是[型變](http://www.kotlincn.net/docs/reference/generics.html#%E5%9E%8B%E5%8F%98)的。這意味著,如果類 `Rectangle` 繼承自 `Shape`,則可以在需要 `List <Shape>` 的任何地方使用 `List <Rectangle>`。換句話說,**集合類型與元素類型具有相同的子類型關系**。 Maps在值類型上是型變的,但在鍵類型上不是。
反之,**可變集合不是型變的;否則將導致運行時故障**。 如果 `MutableList <Rectangle>` 是`MutableList <Shape>` 的子類型,你可以在其中插入其他 `Shape` 的繼承者(例如,`Circle`),從而違反了它的 `Rectangle` 類型參數。
下面是 Kotlin 集合接口的圖表:

讓我們來看看接口及其實現。
### Collection
[`Collection<T>`](https://kotlinlang.org/api/latest/jvm/stdlib/kotlin.collections/-collection/index.html) 是集合層次結構的**根**。此接口表示一個只讀集合的共同行為:檢索大小、檢測是否為成員等等。
`Collection` 繼承自 `Iterable <T>` 接口,它定義了迭代元素的操作。可以使用 `Collection` 作為適用于不同集合類型的函數的參數。對于更具體的情況,請使用 `Collection` 的繼承者: [`List`](https://kotlinlang.org/api/latest/jvm/stdlib/kotlin.collections/-list/index.html) 與 [`Set`](https://kotlinlang.org/api/latest/jvm/stdlib/kotlin.collections/-set/index.html)。
```kotlin
fun printAll(strings: Collection<String>) {
for(s in strings) print("$s ")
println()
}
fun main() {
val stringList = listOf("one", "two", "one")
printAll(stringList)
val stringSet = setOf("one", "two", "three")
printAll(stringSet)
}
```
運行結果
```
one two one
one two three
```
[`MutableCollection`](https://kotlinlang.org/api/latest/jvm/stdlib/kotlin.collections/-mutable-collection/index.html) 是一個具有寫操作的 `Collection` 接口,例如 `add` 以及 `remove`。
```kotlin
fun List<String>.getShortWordsTo(shortWords: MutableList<String>, maxLength: Int) {
this.filterTo(shortWords) { it.length <= maxLength }
// throwing away the articles
val articles = setOf("a", "A", "an", "An", "the", "The")
shortWords -= articles
}
fun main() {
val words = "A long time ago in a galaxy far far away".split(" ")
val shortWords = mutableListOf<String>()
words.getShortWordsTo(shortWords, 3)
println(shortWords)
}
```
運行結果
```
[ago, in, far, far]
```
### List
[`List<T>`](https://kotlinlang.org/api/latest/jvm/stdlib/kotlin.collections/-list/index.html) 以指定的順序存儲元素,并**提供使用索引訪問元素的方法**。索引從 0 開始 – 第一個元素的索引 – 直到最后一個元素的索引即 `(list.size - 1)`。
```kotlin
fun main() {
//sampleStart
val numbers = listOf("one", "two", "three", "four")
println("Number of elements: ${numbers.size}")
println("Third element: ${numbers.get(2)}")
println("Fourth element: ${numbers[3]}")
println("Index of element \"two\" ${numbers.indexOf("two")}")
//sampleEnd
}
```
運行結果
```
Number of elements: 4
Third element: three
Fourth element: four
Index of element "two" 1
```
**List 元素(包括空值)可以重復**:List 可以包含任意數量的相同對象或單個對象的出現。**如果兩個 List 在相同的位置具有相同大小和相同結構的元素,則認為它們是相等的**。
```kotlin
data class Person(var name: String, var age: Int)
fun main() {
//sampleStart
val bob = Person("Bob", 31)
val people = listOf<Person>(Person("Adam", 20), bob, bob)
val people2 = listOf<Person>(Person("Adam", 20), Person("Bob", 31), bob)
println(people == people2)
bob.age = 32
println(people == people2)
//sampleEnd
}
```
運行結果
```
true
false
```
[`MutableList`](https://kotlinlang.org/api/latest/jvm/stdlib/kotlin.collections/-mutable-list/index.html) 是可以進行寫操作的 `List`,例如用于在特定位置添加或刪除元素。
```kotlin
fun main() {
//sampleStart
val numbers = mutableListOf(1, 2, 3, 4)
numbers.add(5)
numbers.removeAt(1)
numbers[0] = 0
numbers.shuffle()
println(numbers)
//sampleEnd
}
```
運行結果
```
[5, 3, 4, 0]
```
如你所見,在某些方面,List 與數組(Array)非常相似。但是,有一個重要的區別:**數組的大小是在初始化時定義的,永遠不會改變; 反之,List 沒有預定義的大小;作為寫操作的結果,可以更改 List 的大小:添加,更新或刪除元素。**
>[info]在 Kotlin 中,`List` 的默認實現是 [`ArrayList`](https://kotlinlang.org/api/latest/jvm/stdlib/kotlin.collections/-array-list/index.html),可以將其視為可調整大小的數組。
### Set
[`Set<T>`](https://kotlinlang.org/api/latest/jvm/stdlib/kotlin.collections/-set/index.html) 存儲**唯一的元素(不可重復)**;它們的**順序通常是未定義的**。`null` 元素也是唯一的:一個 `Set` 只能包含一個 `null`。當兩個 `set` 具有相同的大小并且對于一個 `set` 中的每個元素都能在另一個 `set` 中存在相同元素,則兩個 `set` 相等。
```kotlin
fun main() {
//sampleStart
val numbers = setOf(1, 2, 3, 4)
println("Number of elements: ${numbers.size}")
if (numbers.contains(1)) println("1 is in the set")
val numbersBackwards = setOf(4, 3, 2, 1)
println("The sets are equal: ${numbers == numbersBackwards}")
//sampleEnd
}
```
運行結果
```
Number of elements: 4
1 is in the set
The sets are equal: true
```
[`MutableSet`](https://kotlinlang.org/api/latest/jvm/stdlib/kotlin.collections/-mutable-set/index.html) 是一個帶有來自 `MutableCollection` 的寫操作接口的 `Set`。
`Set`的默認實現 - [`LinkedHashSet`](https://kotlinlang.org/api/latest/jvm/stdlib/kotlin.collections/-linked-hash-set/index.html) – 保留元素插入的順序。因此,依賴于順序的函數,例如 `first()` 或 `last()`,會在這些 `set` 上返回可預測的結果。
```kotlin
fun main() {
//sampleStart
val numbers = setOf(1, 2, 3, 4) // LinkedHashSet is the default implementation
val numbersBackwards = setOf(4, 3, 2, 1)
println(numbers.first() == numbersBackwards.first())
println(numbers.first() == numbersBackwards.last())
//sampleEnd
}
```
運行結果
```
false
true
```
另一種實現方式 – [`HashSet`](https://kotlinlang.org/api/latest/jvm/stdlib/kotlin.collections/-hash-set/index.html) – 不聲明元素的順序,所以在它上面調用這些函數會返回不可預測的結果。但是,**`HashSet` 只需要較少的內存來存儲相同數量的元素**。
### Map
[`Map<K, V>`](https://kotlinlang.org/api/latest/jvm/stdlib/kotlin.collections/-map/index.html) 不是 `Collection` 接口的繼承者;但是它也是 Kotlin 的一種集合類型。
`Map` 存儲 _鍵-值_ 對(或 _條目_);**鍵是唯一的,但是不同的鍵可以與相同的值配對(即值可以重復)**。`Map` 接口提供特定的函數進行通過鍵訪問值、搜索鍵和值等操作。
```kotlin
fun main() {
//sampleStart
val numbersMap = mapOf("key1" to 1, "key2" to 2, "key3" to 3, "key4" to 1)
println("All keys: ${numbersMap.keys}")
println("All values: ${numbersMap.values}")
if ("key2" in numbersMap) println("Value by key \"key2\": ${numbersMap["key2"]}")
if (1 in numbersMap.values) println("The value 1 is in the map")
if (numbersMap.containsValue(1)) println("The value 1 is in the map") // 同上
//sampleEnd
}
```
運行結果
```
All keys: [key1, key2, key3, key4]
All values: [1, 2, 3, 1]
Value by key "key2": 2
The value 1 is in the map
The value 1 is in the map
```
**無論鍵值對的順序如何,包含相同鍵值對的兩個 `Map` 是相等的**。
```kotlin
fun main() {
//sampleStart
val numbersMap = mapOf("key1" to 1, "key2" to 2, "key3" to 3, "key4" to 1)
val anotherMap = mapOf("key2" to 2, "key1" to 1, "key4" to 1, "key3" to 3)
println("The maps are equal: ${numbersMap == anotherMap}")
//sampleEnd
}
```
運行結果
```
The maps are equal: true
```
[`MutableMap`](https://kotlinlang.org/api/latest/jvm/stdlib/kotlin.collections/-mutable-map/index.html) 是一個具有寫操作的 `Map` 接口,可以使用該接口添加一個新的鍵值對或更新給定鍵的值。
```kotlin
fun main() {
//sampleStart
val numbersMap = mutableMapOf("one" to 1, "two" to 2)
numbersMap.put("three", 3)
numbersMap["one"] = 11
println(numbersMap)
//sampleEnd
}
```
運行結果
```
{one=11, two=2, three=3}
```
`Map` 的默認實現 – [`LinkedHashMap`](https://kotlinlang.org/api/latest/jvm/stdlib/kotlin.collections/-linked-hash-map/index.html) – 迭代 Map 時保留元素插入的順序。反之,另一種實現 – [`HashMap`](https://kotlinlang.org/api/latest/jvm/stdlib/kotlin.collections/-hash-map/index.html) – 不聲明元素的順序。
- 前言
- Kotlin簡介
- IntelliJ IDEA技巧總結
- idea設置類注釋和方法注釋模板
- 像Android Studion一樣創建工程
- Gradle
- Gradle入門
- Gradle進階
- 使用Gradle創建一個Kotlin工程
- 環境搭建
- Androidstudio平臺搭建
- Eclipse的Kotlin環境配置
- 使用IntelliJ IDEA
- Kotlin學習路線
- Kotlin官方中文版文檔教程
- 概述
- kotlin用于服務器端開發
- kotlin用于Android開發
- kotlin用于JavaScript開發
- kotlin用于原生開發
- Kotlin 用于數據科學
- 協程
- 多平臺
- 新特性
- 1.1的新特性
- 1.2的新特性
- 1.3的新特性
- 開始
- 基本語法
- 習慣用法
- 編碼規范
- 基礎
- 基本類型
- 包與導入
- 控制流
- 返回與跳轉
- 類與對象
- 類與繼承
- 屬性與字段
- 接口
- 可見性修飾符
- 擴展
- 數據類
- 密封類
- 泛型
- 嵌套類
- 枚舉類
- 對象
- 類型別名
- 內嵌類
- 委托
- 委托屬性
- 函數與Lambda表達式
- 函數
- Lambda表達式
- 內聯函數
- 集合
- 集合概述
- 構造集合
- 迭代器
- 區間與數列
- 序列
- 操作概述
- 轉換
- 過濾
- 加減操作符
- 分組
- 取集合的一部分
- 取單個元素
- 排序
- 聚合操作
- 集合寫操作
- List相關操作
- Set相關操作
- Map相關操作
- 多平臺程序設計
- 平臺相關聲明
- 以Gradle創建
- 更多語言結構
- 解構聲明
- 類型檢測與轉換
- This表達式
- 相等性
- 操作符重載
- 空安全
- 異常
- 注解
- 反射
- 作用域函數
- 類型安全的構造器
- Opt-in Requirements
- 核心庫
- 標準庫
- kotlin.test
- 參考
- 關鍵字與操作符
- 語法
- 編碼風格約定
- Java互操作
- Kotlin中調用Java
- Java中調用Kotlin
- JavaScript
- 動態類型
- kotlin中調用JavaScript
- JavaScript中調用kotlin
- JavaScript模塊
- JavaScript反射
- JavaScript DCE
- 原生
- 并發
- 不可變性
- kotlin庫
- 平臺庫
- 與C語言互操作
- 與Object-C及Swift互操作
- CocoaPods集成
- Gradle插件
- 調試
- FAQ
- 協程
- 協程指南
- 基礎
- 取消與超時
- 組合掛起函數
- 協程上下文與調度器
- 異步流
- 通道
- 異常處理與監督
- 共享的可變狀態與并發
- Select表達式(實驗性)
- 工具
- 編寫kotlin代碼文檔
- 使用Kapt
- 使用Gradle
- 使用Maven
- 使用Ant
- Kotlin與OSGI
- 編譯器插件
- 編碼規范
- 演進
- kotlin語言演進
- 不同組件的穩定性
- kotlin1.3的兼容性指南
- 常見問題
- FAQ
- 與Java比較
- 與Scala比較(官方已刪除)
- Google開發者官網簡介
- Kotlin and Android
- Get Started with Kotlin on Android
- Kotlin on Android FAQ
- Android KTX
- Resources to Learn Kotlin
- Kotlin樣品
- Kotlin零基礎到進階
- 第一階段興趣入門
- kotlin簡介和學習方法
- 數據類型和類型系統
- 入門
- 分類
- val和var
- 二進制基礎
- 基礎
- 基本語法
- 包
- 示例
- 編碼規范
- 代碼注釋
- 異常
- 根類型“Any”
- Any? 可空類型
- 可空性的實現原理
- kotlin.Unit類型
- kotlin.Nothing類型
- 基本數據類型
- 數值類型
- 布爾類型
- 字符型
- 位運算符
- 變量和常量
- 語法和運算符
- 關鍵字
- 硬關鍵字
- 軟關鍵字
- 修飾符關鍵字
- 特殊標識符
- 操作符和特殊符號
- 算術運算符
- 賦值運算符
- 比較運算符
- 邏輯運算符
- this關鍵字
- super關鍵字
- 操作符重載
- 一元操作符
- 二元操作符
- 字符串
- 字符串介紹和屬性
- 字符串常見方法操作
- 字符串模板
- 數組
- 數組介紹創建及遍歷
- 數組常見方法和屬性
- 數組變化以及下標越界問題
- 原生數組類型
- 區間
- 正向區間
- 逆向區間
- 步長
- 類型檢測與類型轉換
- is、!is、as、as-運算符
- 空安全
- 可空類型變量
- 安全調用符
- 非空斷言
- Elvis操作符
- 可空性深入
- 可空性和Java
- 函數
- 函數式編程概述
- OOP和FOP
- 函數式編程基本特性
- 組合與范疇
- 在Kotlin中使用函數式編程
- 函數入門
- 函數作用域
- 函數加強
- 命名參數
- 默認參數
- 可變參數
- 表達式函數體
- 頂層、嵌套、中綴函數
- 尾遞歸函數優化
- 函數重載
- 控制流
- if表達式
- when表達式
- for循環
- while循環
- 循環中的 Break 與 continue
- return返回
- 標簽處返回
- 集合
- list集合
- list集合介紹和操作
- list常見方法和屬性
- list集合變化和下標越界
- set集合
- set集合介紹和常見操作
- set集合常見方法和屬性
- set集合變換和下標越界
- map集合
- map集合介紹和常見操作
- map集合常見方法和屬性
- map集合變換
- 集合的函數式API
- map函數
- filter函數
- “ all ”“ any ”“ count ”和“ find ”:對集合應用判斷式
- 別樣的求和方式:sumBy、sum、fold、reduce
- 根據人的性別進行分組:groupBy
- 扁平化——處理嵌套集合:flatMap、flatten
- 惰性集合操作:序列
- 區間、數組、集合之間轉換
- 面向對象
- 面向對象-封裝
- 類的創建及屬性方法訪問
- 類屬性和字段
- 構造器
- 嵌套類(內部類)
- 枚舉類
- 枚舉類遍歷&枚舉常量常用屬性
- 數據類
- 密封類
- 印章類(密封類)
- 面向對象-繼承
- 類的繼承
- 面向對象-多態
- 抽象類
- 接口
- 接口和抽象類的區別
- 面向對象-深入
- 擴展
- 擴展:為別的類添加方法、屬性
- Android中的擴展應用
- 優化Snackbar
- 用擴展函數封裝Utils
- 解決煩人的findViewById
- 擴展不是萬能的
- 調度方式對擴展函數的影響
- 被濫用的擴展函數
- 委托
- 委托類
- 委托屬性
- Kotlin5大內置委托
- Kotlin-Object關鍵字
- 單例模式
- 匿名類對象
- 伴生對象
- 作用域函數
- let函數
- run函數
- with函數
- apply函數
- also函數
- 標準庫函數
- takeIf 與 takeUnless
- 第二階段重點深入
- Lambda編程
- Lambda成員引用高階函數
- 高階函數
- 內聯函數
- 泛型
- 泛型的分類
- 泛型約束
- 子類和子類型
- 協變與逆變
- 泛型擦除與實化類型
- 泛型類型參數
- 泛型的背后:類型擦除
- Java為什么無法聲明一個泛型數組
- 向后兼容的罪
- 類型擦除的矛盾
- 使用內聯函數獲取泛型
- 打破泛型不變
- 一個支持協變的List
- 一個支持逆變的Comparator
- 協變和逆變
- 第三階段難點突破
- 注解和反射
- 聲明并應用注解
- DSL
- 協程
- 協程簡介
- 協程的基本操作
- 協程取消
- 管道
- 慕課霍丙乾協程筆記
- Kotlin與Java互操作
- 在Kotlin中調用Java
- 在Java中調用Kotlin
- Kotlin與Java中的操作對比
- 第四階段專題練習
- 朱凱Kotlin知識點總結
- Kotlin 基礎
- Kotlin 的變量、函數和類型
- Kotlin 里那些「不是那么寫的」
- Kotlin 里那些「更方便的」
- Kotlin 進階
- Kotlin 的泛型
- Kotlin 的高階函數、匿名函數和 Lambda 表達式
- Kotlin協程
- 初識
- 進階
- 深入
- Kotlin 擴展
- 會寫「18.dp」只是個入門——Kotlin 的擴展函數和擴展屬性(Extension Functions / Properties)
- Kotlin實戰-開發Android