2.9数组(集合)与区间

数组

数组在 Kotlin 中由 Array 类表示,有 getset 方法(通过运算符重载可以由[]调用),以及 size 方法,以及一些常用的函数:

class Array<T> private constructor() {
	val size: Int
	operator fun get(index: Int): T
	operator fun set(index: Int, value: T): Unit
	operator fun iterator(): Iterator<T>
	// ...
}

我们可以给库函数 arrayOf() 传递每一项的值来创建ArrayarrayOf(1, 2,3) 创建了一个[1, 2, 3] 这样的数组。也可以使用库函数 arrayOfNulls() 创建一个指定大小的空Array

或者通过指定Array大小并提供一个通过索引产生数组元素值的工厂函数:

// 创建一个 Array<String> 内容为 ["0", "1", "4", "9", "16"]
val asc = Array(5, {i -> (i * i).toString() })

像我们上面提到的, [] 操作符表示调用 get()set() 函数

注意:和 java 不一样,arrays 在 kotlin 中是不可变的。这意味这 kotlin 不允许我们把 Array<String> 转为 Array<Any> ,这样就阻止了可能的运行时错误(但你可以使用 Array

Kotlin 有专门的类来表示原始类型从而避免过度装箱: ByteArray, ShortArray,IntArray 等等。这些类与 Array 没有继承关系,但它们有一样的方法与属性。每个都有对应的库函数:

val x: IntArray = intArrayOf(1, 2, 3)
x[0] = x[1] + x[2]

map/list

转换 map 的最好办法可能是下面这样:

for ((key, value) in map) {
}

为了让这个可以工作,我们需要通过提供 iterator() 函数序列化呈现 map 通过 component1() 和component1() 函数是把元素成对呈现事实上,标准库提供了这样的扩展:

fun <K, V> Map<K, V>.iterator(): Iterator<Map.Entry<K, V>> = entrySet().iterator()
fun <K, V> Map.Entry<K, V>.component1() = getKey()
fun <K, V> Map.Entry<K, V>.component2() = getValue()

因此你可以用 for 循环方便的读取 map (或者其它数据集合)。

把熟悉值存储在 map 中是一种常见的使用方式,这种操作经常出现在解析 JSON 或者其它动态的操作中。这种情况下你可以使用 map 来代理它的属性。

class User(val map: Map<String, Any?>) {
	val name: String by map
	val age: Int by map
}

在这个例子中,构造函数接受一个 map :

val user = User(mapOf(
	"name" to "John Doe",
	"age" to 25
))

代理属性将从这个 map 中取指(通过属性的名字):

println(user.name) // Prints "John Doe"
println(user.age) // Prints 25

var 属性可以用 MutableMap 代替只读的 Map :

class MutableUser(val map: MutableMap<String, Any?>) {
	var name: String by map
	var age: Int by map
}

集合

与大多数语言不同,Kotlin 区分可变集合和不可变集合(listssetsmaps 等)。精确控制什么时候集合可编辑有助于消除 bug 和设计良好的 API。预先了解一个可变集合的只读视图 和一个真正的不可变集合之间的区别是很重要的。它们都容易创建,但类型系统不能表达它们的差别,所以由你来跟踪(是否相关)。

Kotlin 的 List<out T> 类型是一个提供只读操作如 sizeget 等的接口。和 Java 类似,它继承自 Collection<T> 进而继承自Iterable<T> 。改变 list 的方法是由 MutableList<T> 加入的。这一模式同样适用于 Set<out T>/MutableSet<T>Map<K, outV>/MutableMap<K, V>

我们可以看下 listset 类型的基本用法:

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 没有专门的语法结构创建 listset。 要用标准库的方法,如 listOf()mutableListOf()setOf()mutableSetOf() 。 在非性能关键代码中创建 map 可以用一个简单的惯用法来完成:mapOf(a to b, c to d)

注意上面的 readOnlyView 变量(译者注:与对应可变集合变量 numbers )指向相同的底层 list 并会随之改变。 如果一个 list 只存在只读引用,我们可以考虑该集合完全不可变。创建一个这样的集合的一个简单方式如下:

val items = listOf(1, 2, 3)

目前 listOf 方法是使用 array list 实现的,但是未来可以利用它们知道自己不能变的事实,返回更节约内存的完全不可变的集合类型。

注意这些类型是协变的。这意味着,你可以把一个 List 赋值给 `List` 假定 `Rectangle` 继承自 `Shape`。对于可变集合类型这是不允许的,因为这将导致运行时故障。

有时你想给调用者返回一个集合在某个特定时间的一个快照, 一个保证不会变的:

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 等等。

Map 遵循同样模式。它们可以容易地实例化和访问,像这样:

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

区间

区间表达式由具有操作符形式 ..rangeTo 函数辅以 in!in 形成。 区间是为任何可比较类型定义的,但对于整型原生类型,它有一个优化的实 现。以下是使用区间的一些示例

if (i in 1..10) { // 等同于 1 <= i && i <= 10
	println(i) 
}

整型区间( IntRangeLongRangeCharRange )有一个额外的特性:它们可以迭代。 编译器负责将其转换为类似 Java 的基于索引的 for-循环而无额外开销。

for (i in 1..4) print(i) // 输出“1234”
for (i in 4..1) print(i) // 什么都不输出

如果你想倒序迭代数字呢?也很简单。你可以使用标准库中定义的 downTo() 函数

for (i in 4 downTo 1) print(i) // 输出“4321”

能否以不等于 1 的任意步长迭代数字? 当然没问题, step() 函数有助于此

for (i in 1..4 step 2) print(i) // 输出“13”
for (i in 4 downTo 1 step 2) print(i) // 输出“42”

要创建一个不包括其结束元素的区间,可以使用 until 函数:

for (i in 1 until 10) { // i in [1, 10) 排除了 10
	println(i)
}

使用ranges

for (i in 1..100) { ... } // 闭区间: 包括100
for (i in 1 until 100) { ... } // 半开区间: 不包括100
for (x in 2..10 step 2) { ... } //步长为2
for (x in 10 downTo 1) { ... } //降序循环
if (x in 1..10) { ... } //判断x是否在1到10(闭区间)之间的数中

[上一篇:2.8智能转换] [返回目录] [下一篇:3.1Lambda]