Range Operator in Kotlin is a basic operator that is used to operate over a range. A range can be defined with a start value and an end value with and without inclusion.
The range operators can be used with for loops, if conditions or even in when operator. First, let to see a basic example of a range operator.
A basic range operator can be defined with ..
having a lower range value to the left side and upper range value to the right side of ..
val x = 4
for (item in 1..10){
println(item)
}
if ((1..10).contains(x)){
println("X is in range")
}
when (x){
in 1..10 -> {
println("X is in range")
}
}
Lets try to understand the ..
operator and its return type first.
rangeTo() function
..
is an operator overloading to rangeTo()
function which is defined in kotlin.ranges
package.
rangeTo()
function is an extension function to the Template
class where the Template
class should implement the Comparable
interface.
That means we can create a range to any primitive or non primitive data type which implements the Comparable
interface.
/**
* Creates a range from this [Comparable] value to the specified [that] value.
*
* This value needs to be smaller than or equal to [that] value, otherwise the returned range will be empty.
* @sample samples.ranges.Ranges.rangeFromComparable
*/
public operator fun <T : Comparable<T>> T.rangeTo(that: T): ClosedRange<T> = ComparableRange(this, that)
ClosedRange
The rangeTo()
function returns a ClosedRange
interface object which has a start
and an endInclusive
variables and contains
operator function which is used by the in
operator and an isEmpty
function.
package kotlin.ranges
/**
* Represents a range of values (for example, numbers or characters).
*/
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.
*
* The range is empty if its start value is greater than the end value.
*/
public fun isEmpty(): Boolean = start > endInclusive
}
CharRange, IntRange & LongRange
ClosedRange
is further extended by CharRange
, IntRange
and LongRange
which provides the functionality of Iterable
also along with closed range which is used by [for loops](/blogs/kotlin-for-loop/" target="_blank").
package kotlin.ranges
/**
* A range of values of type `Char`.
*/
public class CharRange(start: Char, endInclusive: Char) : CharProgression(start, endInclusive, 1), ClosedRange<Char> {
override val start: Char get() = first
override val endInclusive: Char get() = last
override fun contains(value: Char): Boolean = first <= value && value <= last
/**
* Checks whether the range is empty.
*
* The range is empty if its start value is greater than the end value.
*/
override fun isEmpty(): Boolean = first > last
override fun equals(other: Any?): Boolean =
other is CharRange && (isEmpty() && other.isEmpty() ||
first == other.first && last == other.last)
override fun hashCode(): Int =
if (isEmpty()) -1 else (31 * first.code + last.code)
override fun toString(): String = "$first..$last"
companion object {
/** An empty range of values of type Char. */
public val EMPTY: CharRange = CharRange(1.toChar(), 0.toChar())
}
}
/**
* A range of values of type `Int`.
*/
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
override fun contains(value: Int): Boolean = first <= value && value <= last
/**
* Checks whether the range is empty.
*
* The range is empty if its start value is greater than the end value.
*/
override fun isEmpty(): Boolean = first > last
override fun equals(other: Any?): Boolean =
other is IntRange && (isEmpty() && other.isEmpty() ||
first == other.first && last == other.last)
override fun hashCode(): Int =
if (isEmpty()) -1 else (31 * first + last)
override fun toString(): String = "$first..$last"
companion object {
/** An empty range of values of type Int. */
public val EMPTY: IntRange = IntRange(1, 0)
}
}
/**
* A range of values of type `Long`.
*/
public class LongRange(start: Long, endInclusive: Long) : LongProgression(start, endInclusive, 1), ClosedRange<Long> {
override val start: Long get() = first
override val endInclusive: Long get() = last
override fun contains(value: Long): Boolean = first <= value && value <= last
/**
* Checks whether the range is empty.
*
* The range is empty if its start value is greater than the end value.
*/
override fun isEmpty(): Boolean = first > last
override fun equals(other: Any?): Boolean =
other is LongRange && (isEmpty() && other.isEmpty() ||
first == other.first && last == other.last)
override fun hashCode(): Int =
if (isEmpty()) -1 else (31 * (first xor (first ushr 32)) + (last xor (last ushr 32))).toInt()
override fun toString(): String = "$first..$last"
companion object {
/** An empty range of values of type Long. */
public val EMPTY: LongRange = LongRange(1, 0)
}
}
downTo
To iterate over a range in reverse order, we can use downTo
operator which is an extension function on different types like Int
, Long
, Char
and provides the reverse iteration with step size as -1
.
val x = 4
for (item in 10 downTo 1){
println(item)
}
if ((10 downTo 1).contains(x)){
println("X is in range")
}
when (x){
in 10 downTo 1 -> {
println("X is in range")
}
}
until
The until
operator can be used to exclude the endElement
. The iterator
or the if
and when
condition will not include the end element in case of until
.
val x = 4
for (item in 1 until 10){
println(item)
}
if ((1 until 10).contains(x)){
println("X is in range")
}
when (x){
in 1 until 10 -> {
println("X is in range")
}
}
step
The step
can be used to change the step size of the iteration over the range.
val x = 4
for (item in 1..10 step 2){
println(item)
}
if ((1 until 10 step 2).contains(x)){
println("X is in range")
}
when (x){
in 10 downTo 1 step 2 -> {
println("X is in range")
}
}