@zhongzilu
2019-02-11T23:56:54.000000Z
字数 2746
阅读 4025
我们最早接触到 “区间” 一词, 应该是数学老师告诉我们的数学意义上的实数范围区间, 它指的是, 从一个实数到另一个实数之间连续的实数集合, 分为 全开区间
、左开右闭区间
、左闭右开区间
、全闭区间
四种. 可以用符号分别表示为 (a, b)
、(a, b]
、[a, b)
、[a, b]
. 而今天我们要讲的 Kotlin 中的区间, 也是基于这个概念.
虽然数学上的区间分为 全开区间
、左开右闭区间
、左闭右开区间
、全闭区间
四种, 但 Kotlin 中的区间却只支持最后两种, 即 左闭右开区间
和 全闭区间
, 因为所有区间都可以用这两种区间来表示, 比如:
区间 | 示例 | 闭区间表示 | 左闭右开表示 |
---|---|---|---|
开区间 | (1, 10) | [2, 9] | [2, 10) |
闭区间 | [1, 10] | [1, 10] | [1, 11) |
左开右闭区间 | (1, 10] | [2, 10] | [2, 11) |
左闭右开区间 | [1, 10) | [1, 9] | [1, 10) |
本篇文章重点讲解以下几个方面, 让大家对 Kotlin 中的区间, 以及如何运用有所了解.
- 它是什么?
- 它的优点是什么?
- 它的原理是什么?
- 如何使用?
Kotlin 中的区间, 具体指的是 ClosedRange
接口, 以及它的实现类 IntRange
、LongRange
、CharRange
. 你可以通过这三个类的构造方法来创建一个区间, 构造方法如下:
IntRange(start: Int, endInclusive: Int)
LongRange(start: Long, endInclusive: Long)
CharRange(start: Char, endInclusive: Char)
但本文的重点不是通过构造方法创建区间, 而是另外一种方式, Kotlin 中有一种更简单的方式创建区间, 那就是通过区间操作符 ..
来创建, 比如创建一个包含 1
和 10
的闭区间, 相当于数学里的表达式 [1, 10]
, 代码如下:
val range = 1..10
是不是很简单? 该区间操作符在操作符重载相关文章中有提到. 使用该操作符最终会被编译为调用它对应的方法函数 rangeTo()
.
具体用法请看本文的 [ 如何使用 ] 章节的内容.
它的优点就是可以通过简单的符号来创建一个区间, 而不需要通过构造函数那样繁琐的方式. 另外, 区间操作符配合 in
操作符, 可以实现区间的迭代, 也可以用来作为判断条件.
使用区间操作符 ..
来创建区间, 会在编译期间编译成调用相应的 rangeTo()
方法函数, 返回的实例就是 IntRange
、LongRange
、 CharRange
三个中一个, 比如 1..10
这样的表达式就是返回的 IntRange
类型的实例, 而 1L..10L
这样的表达式则返回的是 LongRange
类型的实例.
简单的来说, 它最终还是通过构造方法的方式来创建的区间, 区间操作符来简化了我们自己实例化的过程.
最基本的使用, 比如用来判断某个数值 i
是否在 [1, 10] 内 , 用 Java 的方式大概如下:
if (i >= 1 && i <= 10) System.out.println(i);
如果换做 Kotlin 的区间表示的话, 代码大概如下:
if (i in 1..10) println(i)
直接判断数值 i
是否在区间内, 这是很直观的思维逻辑. 这里使用了 in
操作符, 之所以能用 in
操作符, 那是因为区间的共同接口 CloseRange<T>
重载了 in
操作符的对应方法函数 contains()
:
public operator fun contains(value: T): Boolean = value >= start && value <= endInclusive
而这里 1..10
表达式实际上创建的是 IntRange
实例, IntRange
就继承自该接口, 所以能使用 in
操作符.
整型区间 (IntRange、LongRange、CharRange)
有⼀个额外的特性:它们可以迭代。编译器会将其转换为类似 Java 的基于索引的 For
循环, 因此⽆额外的开销:
for (i in 1..10) print(i) // 输出: "12345678910"
除了使用 For
循环的写法, 还可以使用 Kotlin 自带的 forEach
函数来对区间进行迭代, 写法如下:
(1..10).forEach { print(it) } // 输出: "12345678910"
用括号 ()
将区间表达式包裹住, 就可以使用 forEach
函数了. 当然, 只有整型区间才可以迭代.
如果你想创建一个不包含最后一项元素的区间, 例如 [1, 10)
, 那么可以使用 until
中缀操作符来实现, 如果不了解中缀操作符, 可以先去看看中缀操作符相关的文章. 代码如下:
for (i in 1 until 10) print(i) // 输出: "123456789"
由于 Kotlin 中的区间, 都是左封闭区间, 如果你想创建一个不包含第一项和最后一项元素的区间, 即数学里的 全开区间
, 那么就需要通过取巧的方式, 比如 (1, 10)
表示的区间, 代码可以如下:
// 第一种方式: 转换成闭区间来表示
for (i in 2..9) print(i) // 输出: "23456789"
// 第二种方式: 转换成左闭右开区间来表示
for (i in 2 until 10) print(i) // 输出: "23456789"
如果你想创建一个倒序的区间, 可以使用 downTo
中缀操作符, 也可以使用 reversed()
方法函数, 例如:
// 第一种方式: 使用 downTo 中缀操作符
for (i in 10 downTo 1) print(i) // 输出: "10987654321"
// 第二种方式: 使用 reversed() 函数
(1..10).reversed()
downTo
中缀操作符, 会包含区间的第一项和最后一项, 可以看作是一个 闭区间
. 而 reversed()
函数操作的区间, 取决于它的调用者, 它是负责将区间进行反序而已.
如果你想在进行区间迭代的时候加上一个步进值, 那么可以使用 step
中缀操作符, 代码如下:
for (i in 1..10 step 2) print(i) // 输出: "13579"
step
操作符是使用扩展函数的方式, 专为区间类型扩展的. 所以也只有区间类型才能使用.
Kotlin 中的区间, 相当于数学概念中的闭区间, 只支持 左闭右开区间
和 全闭区间
, 因为所有的区间都能用这两种区间来表示;
创建一个区间, 可以通过 IntRange
、 LongRange
、 CharRange
三者的构造函数的方式来创建, 也可以通过使用区间操作符 ..
来创建, 区间操作符 ..
创建的是一个 全闭区间
, 而使用中缀 until
可以用来创建一个 左闭右开区间
.
使用区间操作符的优点就是可以很简单的创建一个区间, 并且它可以应用到 For
循环迭代和 if
条件判断中.
在 For
循环迭代中, 可以通过使用中缀 downTo
或者调用 reversed()
方法函数来创建倒序的区间, 实现区间的倒序输出. 也可以使用中缀 step
来设置迭代的步进值.