Kotlin进阶之集合与区间
2017-09-23 14:39
169 查看
集合
与多数语言不一样,Kotlin区分可变与不可变集合(list,sets,maps等等)。精确控制什么时候可以编辑集合有利于减少BUG和设计良好的API。在这之前,理解只读的可变集合与实际不变的集合的区别很有必要。两者创建都很容易,但是类型系统表达却不相同。
Kotlin的List<out T>类型是一个接口,提供只读操作如size,get等等。与Java相同,继承Collection<T>接口,Collection<T>继承Iterable<T>。MutableList<T>添加了修改数组的方法。同样模式还有Set<out T>/MutableSet<T>和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(numbers) // 输出"[1 , 2, 3, 4]" readOnlyView.clear() // 不会通过编译 val strings = hashSetOf("a", "b", "c", "c") assert(strings.size == 3)
Kotlin没有创建列表或Set的专用语法,可以使用如listOf(),mutableListOf(),setOf(),mutableSetOf()创建。在非性能严格的代码中,可以使用简单的惯用语法实现:mapOf(a
to b, c to d)。
readOnlyView变量指向相同list,像基本List改变一样改变。如果唯一引用指向只读变量List,则认为集合整体不可变。
创建不可变集合的一个简单方法:
val items = listOf(1, 2, 3)
目前,listOf方法使用数组List实现,但在未来可能会返回存储效率更高的完全不可变的集合类型。
只读类型为协变量,意味可是使用List<Rectangle>赋值给List<Shape>(假设Rectangle继承Shape)。在可变集合中则不被允许,因为在运行时,可变集合可能允许出错。
有时希望及时返回集合快照给调用者,保证集合不变。
class Controller { private val _items = mutableListOf<String>() val items: List<String> get() = _items.toList() }
toList扩展函数只是List的副本,返回的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()
同样的还有其他如排序,压缩,折叠,减少等可能需要的实用方法。
Map采取相同模式,可以像如下实例化和访问。
val readWriteMap = hashMapOf("foo" to 1, "bar" to 2) println(readWriteMap["foo"]) // 输出"1" val snapshot: Map<String, Int> = HashMap(readWriteMap)
区间
使用rangeTo函数行成的区间表达式,可以使用..形式的操作符,并使用in或!in补全。Kotlin为任意比较类型定义的区间,对于整型基本类型则有自己最优化的实现。如:if( i in 1..10){ // 与 1 <= i && i <= 10相等 println(i) }
整形类型区间(IntRange,LongRange,CharRange)有个额外的特性:可以循环迭代。编译器可以将区间转为近似Java的for循环,而没有额外的开销。
for ( i in 1..4) print(i) // 输出 "1234" for (i in 1..4) print(i) // 无任何输出
如果希望逆序循环,可通过标准库中定义的downTo函数
for (i in 4 downTo 1) print(i) // 输出 "4321"
循环通过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) }
实现原理
区间实现库中的一个通用的接口:ClosedRange<T>ClosedRange<T>表示数学意义上的闭合区间,为比较类型定义。有两个端点:起始和结束,两点都包含在区间内。主要操作符是contains,使用形式in和!in
整型数列(IntProgression,LongProgression,CharProgression)表示算数队列。数列使用起始元素,末尾元素和非零步数定义。首位元素为起始元素,子队列元素为前一个元素加上步数,末尾元素总要循环命中(除非队列为空)。
队列为Iterable<N>子类,N表示Int,Long,Char,所以可以在for循环和像map,filter等函数中使用。队列循环近似与Java中的索引for循环
for (int i = first; i != last; i += step) { // ... }
对于整型,..操作符会创建实现ClosedRange<T>和*Progression接口的对象。如IntRange实现ClosedRange<Int>并继承IntProgression,因此所有为IntProgression定义的操作符都用于IntRange。downTo()和step()函数返回值为*Progression。
使用fromClosedRange函数构建的队列,定义在它们的伴生对象中:
IntProgression.fromClosedRange(start, end, step)
队列末尾元素用于计算查找step为正数时不超过end值的最大值,或step为负数是不小于end的最小值(last - first) % step == 0。
工具函数
rangeTo()
整型的rangeTo()操作符就是调用*Range的构造方法class Int{ //... operator fun rangeTo(other: Long): LongRange = LongRange(this, other) //... operator fun rangeTo(other: Int): IntRange = IntRange(this, other) //... }
浮点数(Double,Float)没有定义rangeTo操作符,使用标准库提供的泛型Comparable类型替代。
public operator fun <T: Comparable<T>> T.rangeTo(that: T): ClosedRange<T>
这个函数的返回区间不能用于循环迭代。
downTo()
为整型定义的downTo() 扩展函数,如:fun Long.downTo(other: Int): LongProgression { return LongProgression.fromClosedRange(this, other.toLong(), -1L) } fun Byte.downTo(other: Int): IntProgression { return IntProgression.fromClosedRange(this.toInt(), other, -1) }
reversed()
为每个*Progression类定义reversed()扩展函数,返回反转的数列fun IntProgression.reversed(): IntProgression { return IntProgression.fromClosedRange(last, first, -step) }
step()
为*Progression定义step()扩展函数,返回使用step值过滤后的数列。step值要求总要为正数,因此这个不会修改循环方向。fun IntProgression.step(step: Int): IntProgression { if (step <= 0) throw IllegalArgumentException("Step must be positive, was: $step") return IntProgression.fromClosedRange(first, last, if (this.step > 0) step else -step) } fun CharProgression.step(step: Int): CharProgression { if (step <= 0) throw IllegalArgumentException("Step must be positive, was: $step") return CharProgression.fromClosedRange(first, last, if (this.step > 0) step else -step) }
函数返回的数列末尾值可能会与原队列末尾值不同,保证(last - first) % step == 0。
(1..12 step 2).last == 11 // 队列 [1, 3, 5, 7, 9, 11] (1..12 step 3).last == 10 // 队列 [1, 4, 7, 10] (1..12 step 4).last == 9 // 队列 [1, 5, 9]
相关文章推荐
- android kotlin其它(二)集合、区间、操作符
- Kotlin学习(二)—— 基本语法,函数,变量,字符串模板,条件表达式,null,类型检测,for,while,when,区间,集合
- Kotlin学习(二)—— 基本语法函数变量,字符串模板条件表达式,null,类型检测,for,while,when,区间,集合
- Kotlin-26.集合(Collection)
- Kotlin学习笔记5-2 其他-集合
- Kotlin入门篇(五),Loop和Range,Kotlin中区间的定义和遍历
- hibernate进阶之集合关系映射
- Kotlin使用lambda表达式过滤和映射集合:
- Kotlin Primer · 集合泛型与操作符
- kotlin集合操作符——映射操作符
- 51nod 1672 区间交 (优先队列priority_queue 或 多重集合multiset)
- Kotlin学习--集合操作符之总数操作符
- Kotlin学习--集合操作符之顺序操作符
- JAVA进阶4.10——集合类的比较
- Csharp进阶:泛型集合
- java进阶(三):反射(3)——数组的反射与集合的运用(ArrayList、HashSet)
- kotlin list集合api扩展操作
- 数学之路-数据分析进阶-区间估计与假设检验(2)
- Kotlin学习记录(四)—— 常用集合的使用
- Kotlin提供的集合本地接口