什么是Range
- Range是Kotlin相对Java新增的一种表达式,它表示的是值的范围,类似于数学中的区间。
- Range的表达式是像这样子的:
1..20,其中..是运算符,它表示一个闭区间[1, 20]。而右开区间用until表示:1 until 20,即[1, 20)。 - Range表达式一般是和
in和!in操作符一起使用,表示是否包含在该区间内,例如:if (i in 1..20){ //相当于 i >= 1 && i <= 20 ... } - 对于一些整形的range(
IntRange、LongRange、CharRange)是可以进行迭代的,它们可以和for循环一起使用,例如:Kotlin 1.1以后新增了for (i in 1..4) print(i) // 输出 "1234" for (i in 4..1) print(i) // 因为"4..1"这个区间为空,所以什么都没有输出Double和Float的range,但是它们只能进行in和!in操作,不能对它们进行迭代。 - 使用
downTo()函数可以对range进行倒序迭代,例如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"
How it works
range是如何实现和工作的呢?我们知道1..20这个表达式是Int中实现了rangeTO()操作符,它等价于1.rangTo(20),返回一个IntRange(1, 20),Kotlin中的源码如下
class Int {
//...
operator fun rangeTo(other: Long): LongRange = LongRange(this, other)
//...
operator fun rangeTo(other: Int): IntRange = IntRange(this, other)
//...
}下面将以IntRange为例,简单分析Range的实现和工作。下图为IntRange的类图:

IntRange实现了ClosedRange<T>接口,该接口需要传入一个实现了Comparable<T>接口的范型,对于IntRange来说就是Int。ClosedRange<T>就相当于上面说的闭区间,区间的两个端点分别是接口中的两个参数:start和endInclusive,最主要的是它的contains()函数。start和endInclusive必须要实现该接口的类去override,而contains()已经在ClosedRange中实现了,则不需要进行重写。这也是Kotlin和Java不同的地方之一:接口中可以有方法实现,也可以只定义方法签名;也可以有自己的属性,但是不能对属性进行初始化,必须由实现它的类进行初始化,否则抽象类就要下岗了。以下是该接口的源码:
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
}IntRange中给两个端点进行赋值,代码如下:
public class IntRange(start: Int, endInclusive: Int) : IntProgression(start, endInclusive, 1), ClosedRange<Int> {
override val start: Int get() = first
override val endInclusive: Int get() = last
// ...
}这个first和last是什么东西?点进去之后,发现是它的父类IntProgression中的值。
public open class IntProgression
internal constructor
(
start: Int,
endInclusive: Int,
step: Int
) : Iterable<Int> {
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)
// ...
companion object {
public fun fromClosedRange(rangeStart: Int, rangeEnd: Int, step: Int): IntProgression = IntProgression(rangeStart, rangeEnd, step)
}
}IntProgression的作用主要有两个:
- 确定迭代时区间中的最后一个值
last,由于迭代时step可以不为1,这个值有可能不等于区间右边的值。例如for(i in 1..20 step 3),最后一个值应该是19。该值通过progressionUtil中的函数计算得来。 迭代功能的真正实现。
IntProgression实现了Iterable<T>,这个接口就是用来实现迭代功能。重写接口的iterator()函数,返回一个迭代器,这个迭代器必须实现Iterator<T>。IntPresssion返回一个IntProgressionIterator的迭代器,迭代需要的hasNext()和next()真正实现就在这个类里。internal class IntProgressionIterator(first: Int, last: Int, val step: Int) : IntIterator() { private val finalElement = last private var hasNext: Boolean = if (step > 0) first <= last else first >= last private var next = if (hasNext) first else finalElement override fun hasNext(): Boolean = hasNext override fun nextInt(): Int { val value = next if (value == finalElement) { if (!hasNext) throw kotlin.NoSuchElementException() hasNext = false } else { next += step } return value } }public abstract class IntIterator : Iterator<Int> { override final fun next() = nextInt() /** Returns the next value in the sequence without boxing. */ public abstract fun nextInt(): Int }public interface Iterator<out T> { public operator fun next(): T public operator fun hasNext(): Boolean }总结:
IntRange实现的接口ClosedRange实现了区间功能,父类IntProgression实现了迭代功能。LongRange和CharRange原理和IntRange相同。我们只要实现了这个两个接口,就可以定义自己range,并对它进行迭代操作。
自定义range
- 创建一个实现了
Comparable<T>接口的类,这个类就是区间里的元素,相当于前面介绍的Int。重写compareTo操作符函数,重写该方法之后,就可以使用>、<运算符对该类进行运算。这里我们以一个MyDate的日期类为例:data class MyDate(val year: Int, val month: Int, val dayOfMonth: Int) : Comparable<MyDate> { override operator fun compareTo(date: MyDate): Int { if (year != date.year) { return year - date.year } else if (month != date.month) { return month - date.month } else { return dayOfMonth - date.dayOfMonth } } }println(MyDate(2016, 11, 11) > MyDate(2017, 10, 10)) // 输出 false 创建一个range类,实现
ClosedRange<T>接口,因为使用Mydate进行比较,这里的T需要传入MyDate。给Mydate添加rangeTo操作符函数。class DateRange(override val endInclusive: MyDate, override val start: MyDate) :ClosedRange<MyDate>{ // ... }operator fun MyDate.rangeTo(other:MyDate) = DateRange(this, other)这里通过扩展函数的方式,为
MyDate实现rangeTo操作符函数。返回一个DateRange。调用如下:val first = MyDate(2016, 11, 11) val second = MyDate(2017, 10, 10) val other = MyDate(2017, 1, 1) println(other in first..second) // 输出 true- 让
DateRange实现Iterable<MyDate>的接口,重写接口中的iterator()方法,返回一个Iterator<MyDate>对象,这里我们返回一个实现Iterator<T>接口的DateInterator对象,该对象真正实现了迭代的hasNext()和next()方法,实现迭代功能:class DateRange( override val start: MyDate, override val endInclusive: MyDate) : Iterable<MyDate>, ClosedRange<MyDate> { override fun iterator(): Iterator<MyDate> = DateIterator(start, endInclusive) }class DateIterator(first: MyDate, val last: MyDate) : Iterator<MyDate> { var hasNext = first <= last var next = if (hasNext) first else last override fun hasNext(): Boolean = hasNext override fun next(): MyDate { val result = next next = next.addOneDay() hasNext = next <= last return result } }fun MyDate.addOneDay():MyDate{ val c = Calendar.getInstance() c.set(this.year, this.month, this.dayOfMonth) c.add(Calendar.DAY_OF_MONTH, 1) return MyDate(c.get(Calendar.YEAR), c.get(Calendar.MONTH), c.get(Calendar.DAY_OF_MONTH)) }
其他的函数
downTo()
整形如Int、Long中可以调用,如下:
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)
}作用就是迭代的时候,step为-1,例如:
for(i in 5.downTo(1)){
print(i) //输出 5 4 3 2 1
}reversed()
是*Progression类中的扩展方法,返回一个倒序的序列
fun IntProgression.reversed(): IntProgression {
return IntProgression.fromClosedRange(last, first, -step)
}for (i in (5..1).reversed()){
print(i) //输出1 2 3 4 5
}step()
也是定义在*Progession类的扩展方法中,修改迭代的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)
}要注意的是,该方法返回的*Progression中的last值可能不是原来的last值。因为要保证(last - first) % step == 0。例如:
(1..12 step 2).last == 11 // progression with values [1, 3, 5, 7, 9, 11]
(1..12 step 3).last == 10 // progression with values [1, 4, 7, 10]
(1..12 step 4).last == 9 // progression with values [1, 5, 9]