Kotlin学习笔记(十一)集合,区间

集合

Kotlin中,区别可变集合和不可变集合(lists,sets,maps等),不可变集合只提供了有关读的api,没有编辑的api,如果我们要修改集合,只能通过可变集合去修改。这样的设计有助于我们降低bug和设计良好的api。

fun foo(){
    var readOnlyList:List//声明一个只读list
    var mutableList= mutableListOf(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//声明一个制度set
    var mutableSet= mutableSetOf(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//声明一个只读map
    var mutableMap= mutableMapOf()//创建一个可变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接口。

public class IntRange(start: Int, endInclusive: Int) : IntProgression(start, endInclusive, 1), ClosedRange {
//...
}

整型数列(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 {//实现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 分别为 Int、 Long 或者 Char,所以它可用于 for-循环以及像 map、filter 等函数中。

我们再来看ClosedRangeClosedRange在数学意义上表示一个闭区间,它是为可比较类型定义的。 它有两个端点: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> {
    /**
     * 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

你可能感兴趣的:(Kotlin学习笔记)