什么是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]复制代码