什麼是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迴圈一起使用,例如:for (i in 1..4) print(i) // 輸出 "1234" for (i in 4..1) print(i) // 因為"4..1"這個區間為空,所以什麼都沒有輸出複製程式碼
Kotlin 1.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]複製程式碼