集合
Kotlin中,区别可变集合和不可变集合(lists,sets,maps等),不可变集合只提供了有关读的api,没有编辑的api,如果我们要修改集合,只能通过可变集合去修改。这样的设计有助于我们降低bug和设计良好的api。
fun foo(){
var readOnlyList:List<Int>//声明一个只读list
var mutableList= mutableListOf<Int>(1,2,3)//创建一个可变list并初始化
readOnlyList=mutableList//赋值可变list给只读list,这样两者指向相同的底层list,只读list会随着可变list的改变而改变
print(readOnlyList)//[1,2,3]
mutableList.add(4)//通过可变list修改底层list
print(readOnlyList)//[1,2,3,4]
var readOnlySet:Set<Int>//声明一个制度set
var mutableSet= mutableSetOf<Int>(1,2,3)//创建一个可变set并初始化
readOnlySet=mutableSet//赋值可变set给只读set,这样两者指向相同的底层set,只读set会随着可变set的改变而改变
print(readOnlySet)//[1,2,3]
mutableSet.add(4)//通过可变list修改底层list
print(readOnlySet)//[1,2,3,4]
var readOnlyMap:Map<Int,Int>//声明一个只读map
var mutableMap= mutableMapOf<Int,Int>()//创建一个可变Map
readOnlyMap=mutableMap//赋值可变map给只读map,这样两者指向相同的底层map,只读map会随着可变map的改变而改变
mutableMap.put(0,1)
mutableMap.put(1,2)
mutableMap.put(2,3)
for ((key,value) in readOnlyMap){
print("$value,")//1,2,3,
}
}
如果我们只想要一个不可变的集合,可以这样创建:
var list= arrayListOf<Int>(1,2,3)//创建一个不可变的list并初始化
var set= setOf<Int>(1,2,3)//创建一个不可变的set并初始化
var map= hashMapOf<Int,Int>(0 to 1,1 to 2,2 to 3)//创建一个不可变的map并初始化
也可以生成一个集合在当前时刻的一个快照,返回一个保障不会变的集合:
var copyList=mutableList.toList()//toList方法只是复制列表项,因此返回的 list 保证永远不会改变。
var copySet=mutableSet.toSet()//toSet方法只是复制列表项,因此返回的 set 保证永远不会改变。
var copyMap=mutableMap.toMap()//oMap方法只是复制列表项,因此返回的 map 保证永远不会改变。
集合中还有很多其它有用的扩展方法,我们可以到源码中去查看。
区间
区间表达式由具有操作符..
形式的rangeTo函数辅以in和!in形成。 区间是为任何可比较类型定义的,但对于整型原生类型,它有一个优化的实现。例如我们要输出1到10之间的一串数字,可以这样写:
fun printNums(){
for (num in 1..10){
print(num)
}
}
想要输出10到1?我们可以这样写:
fun printNums(){
for (num in 10..1){//错误
print(num)
}
for (num in 10 downTo 1){
print(num)
}
}
或者我们想要隔两个数字输出一个,可以这样写:
fun printNums(){
for (num in 1..10 step 2){//步长为2
print(num)
}
}
以上区间都是包含其结束元素的,如果我们不想输出其结束元素,我们可以这样实现:
fun printNums(){
for (num in 1 until 10 step 2){//until 不包含结束元素
print(num)
}
}
实际上我们Ctrl+鼠标左键
进入源码可以看到,..
操作符实际上等于整数类型的rangTo()函数,该函数返回类型为IntRange
public operator fun rangeTo(other: Int): IntRange
而从IntRange的源码中我们可以看到,IntRange 扩展自IntProgression开放类和ClosedRange<T>
接口。
public class IntRange(start: Int, endInclusive: Int) : IntProgression(start, endInclusive, 1), ClosedRange<Int> {
//...
}
整型数列(IntProgression、 LongProgression、 CharProgression)表示等差数列。 数列由 first 元素、last 元素和非零的 step 定义。 第一个元素是 first,后续元素是前一个元素加上 step。 last 元素总会被迭代命中,除非该数列是空的。我们看IntPression的源码:
/**
* A progression of values of type `Int`.
*/
public open class IntProgression
internal constructor
(
start: Int,
endInclusive: Int,
step: Int
) : Iterable<Int> {//实现Iterable接口
init {
if (step == 0) throw kotlin.IllegalArgumentException("Step must be non-zero")
}
/**
* The first element in the progression.
*/
public val first: Int = start
/**
* The last element in the progression.
*/
public val last: Int = getProgressionLastElement(start.toInt(), endInclusive.toInt(), step).toInt()
/**
* The step of the progression.
*/
public val step: Int = step
override fun iterator(): IntIterator = IntProgressionIterator(first, last, step)
/** Checks if the progression is empty. */
public open fun isEmpty(): Boolean = if (step > 0) first > last else first < last
override fun equals(other: Any?): Boolean =
other is IntProgression && (isEmpty() && other.isEmpty() ||
first == other.first && last == other.last && step == other.step)
override fun hashCode(): Int =
if (isEmpty()) -1 else (31 * (31 * first + last) + step)
override fun toString(): String = if (step > 0) "$first..$last step $step" else "$first downTo $last step ${-step}"
companion object {
/**
* Creates IntProgression within the specified bounds of a closed range.
* The progression starts with the [rangeStart] value and goes toward the [rangeEnd] value not excluding it, with the specified [step].
* In order to go backwards the [step] must be negative.
*/
public fun fromClosedRange(rangeStart: Int, rangeEnd: Int, step: Int): IntProgression = IntProgression(rangeStart, rangeEnd, step)
}
}
我们看到,数列是 Iterable<N>
的子类型,其中 N 分别为 Int、 Long 或者 Char,所以它可用于 for-循环以及像 map、filter 等函数中。
我们再来看ClosedRange<T>
,ClosedRange<T>
在数学意义上表示一个闭区间,它是为可比较类型定义的。 它有两个端点:start 和 endInclusive 他们都包含在区间内。 其主要操作是 contains,通常以 in/!in 操作符形式使用。
/**
* Represents a range of values (for example, numbers or characters).
* See the [Kotlin language documentation](http://kotlinlang.org/docs/reference/ranges.html) for more information.
*/
public interface ClosedRange<T: Comparable<T>> {
/**
* The minimum value in the range.
*/
public val start: T
/**
* The maximum value in the range (inclusive).
*/
public val endInclusive: T
/**
* Checks whether the specified [value] belongs to the range.
*/
public operator fun contains(value: T): Boolean = value >= start && value <= endInclusive
/**
* Checks whether the range is empty.
*/
public fun isEmpty(): Boolean = start > endInclusive
}
我们再看downTo()函数的源码:
public infix fun Int.downTo(to: Int): IntProgression {
return IntProgression.fromClosedRange(this, to, -1)
}
再看fromClosedRange()方法,发现其是IntPression伴生对象的一个方法,返回一个IntPression类型:
public open class IntProgression
companion object {
/**
* Creates IntProgression within the specified bounds of a closed range.
* The progression starts with the [rangeStart] value and goes toward the [rangeEnd] value not excluding it, with the specified [step].
* In order to go backwards the [step] must be negative.
*/
public fun fromClosedRange(rangeStart: Int, rangeEnd: Int, step: Int): IntProgression = IntProgression(rangeStart, rangeEnd, step)
}
}
有一些其它的方法需要我们了解:
util()
util()是所有整数类型和Char类型的扩展函数,底层调用的..
操作符,去除了最后一个区间值
public infix fun Int.until(to: Int): IntRange {
if (to <= Int.MIN_VALUE) return IntRange.EMPTY
return this .. (to - 1).toInt()//to-1 去除最后一个区间值
}
reversed()
reversed()是为所有*Progression类型定义的扩展函数,用于反转一个数列
/**
* Returns a progression that goes over the same range in the opposite direction with the same step.
*/
public fun CharProgression.reversed(): CharProgression {
return CharProgression.fromClosedRange(last, first, -step)//原数列的last元素作为作为新数列的first元素,原数列的first元素作为新数列的first元素,并且step值取负
}
step()
扩展函数 step() 是为每个 *Progression 类定义的, 所有这些函数都返回带有修改了 step 值(函数参数)的数列。 步长(step)值必须始终为正,因此该函数不会更改迭代的方向。
/**
* Returns a progression that goes over the same range with the given step.
*/
public infix fun CharProgression.step(step: Int): CharProgression {
checkStepIsPositive(step > 0, step)
return CharProgression.fromClosedRange(first, last, if (this.step > 0) step else -step)//step值永远为正
}
注意:数列的 last 元素这样计算:对于正的 step 找到不大于 end 值的最大值、或者对于负的 step 找到不小于 end 值的最小值,使得 (last - first) % increment == 0。因此,step值不同,返回数列的 last 值可能与原始数列的 last 值不同。例如:
(1..12 step 2).last == 11
(1..12 step 3).last == 10
(1..12 step 4).last == 9