skip to content

Ranges & Progressions

Ranges and progressions are first-class types you build with operators and then iterate or test membership against. They come up in loops, validation, and character classification constantly.

part ofKotlinoverview, primer and where to startread it →
on this pageshow

explore

questions

15

What is an IntRange in Kotlin, and how does it relate to IntProgression? How do you iterate one in a for-loop?

level: juniorimportance: must knowfreq 70%

answer

  1. IntRange = inclusive [first, last], step always 1
  2. IntRange extends IntProgression
  3. Progression = first, last, step; Iterable<Int>
  4. .. is rangeTo; for (i in 1..10)
  5. Long/CharRange are the siblings

basics

~20 s

An IntRange is a span of integers from a start to an end value, like 1 to 10. You can loop over it with a for-loop, and each step it gives you the next number.

solid answer

~30 s

An IntRange represents a closed (inclusive) range of Int values defined by a start and endInclusive bound, created with the `..` operator or `rangeTo`. It is a subtype of IntProgression, which adds a `step` (default 1) and exposes `first`, `last`, and `step`. Because IntProgression implements `Iterable<Int>`, an IntRange can be used directly in a for-loop: `for (i in 1..10) { ... }`. Iteration yields first, first+step, ... up to and including last. IntRange also implements `ClosedRange<Int>` (and `OpenEndRange<Int>`) so it supports `contains`/`in`. Sibling LongRange and CharRange behave the same for Long and Char.

code

kotlin · 11 lines
kotlin
val range: IntRange = 1..10
println(range.first)  // 1
println(range.last)   // 10
println(range.step)   // 1

// IntRange IS an IntProgression
val p: IntProgression = range

for (i in 1..5) print(i)  // 12345

println((5..1).isEmpty()) // true: start > end

go deeper

for a junior

Knows .. makes an inclusive range and that you loop with for (i in 1..10).

for a middle

Explains IntRange extends IntProgression and that first/last/step exist with step defaulting to 1.

for a senior

Notes IntRange also implements ClosedRange/OpenEndRange, that the loop is often allocation-free, and that 5..1 is empty.

for a principal

Can reason about the dual interface inheritance (progression Iterable + ClosedRange membership) and the compiler loop-lowering optimization implications.

## What an IntRange is An `IntRange` is a Kotlin standard-library type representing a **closed (inclusive) interval** of `Int` values: it has a start and an `endInclusive`, both included. You create one most often with the `..` range operator, which is sugar for the `rangeTo` function: ```kotlin val r: IntRange = 1..10 // 1, 2, ..., 10 (both ends included) println(r.first) // 1 println(r.last) // 10 println(r.step) // 1 ``` ## Relationship to IntProgression The class hierarchy is the key idea: - `IntProgression` is an **arithmetic progression** of `Int`s defined by three numbers: `first`, `last`, and `step` (a non-zero step). It implements `Iterable<Int>`. - `IntRange` is a **subclass** of `IntProgression` whose `step` is always `1`. It *also* implements `ClosedRange<Int>` and `OpenEndRange<Int>`, which is what gives it `contains`/membership semantics. So every `IntRange` is an `IntProgression`, but not every progression is a range. A `2..10 step 2` produces an `IntProgression` (step 2), not an `IntRange`. ## Iterating in a for-loop Because `IntProgression` implements `Iterable<Int>`, you can put a range directly after `in`: ```kotlin for (i in 1..5) print(i) // 12345 ``` The compiler frequently **optimizes** `for (i in a..b)` into a plain counting loop with no allocation or iterator object, but semantically it iterates `first`, `first + step`, ... while the value has not passed `last`. ## Other range types - `LongRange` / `LongProgression` — same shape for `Long`. - `CharRange` / `CharProgression` — same shape for `Char`, e.g. `'a'..'z'`. ## Empty ranges If the start is greater than the end (with a positive step), the range is **empty**: `(5..1).isEmpty()` is `true`, and the for-loop body never runs. `IntRange.EMPTY` is a canonical empty instance. ## Key takeaways - `..` creates an inclusive `IntRange`. - `IntRange` extends `IntProgression`; progressions carry `first`, `last`, `step`. - Progressions are `Iterable`, so they work in for-loops, often without allocation.

  • Is 1..10 inclusive or exclusive of 10?
    Inclusive — IntRange is a closed range, so both 1 and 10 are produced. Use 1 until 10 (or 1..<10) for an exclusive upper bound.
  • What's the type of 2..10 step 2?
    IntProgression, not IntRange, because the step is no longer 1.

A progression is like setting a metronome (start, end, tick interval); an IntRange is that metronome locked to a tick of 1.

saying these in an interview costs you the question

  • Saying IntRange is exclusive of the upper bound
  • Claiming IntRange and IntProgression are unrelated types
  • Thinking a for-loop over a range always allocates an iterator object
  • Confusing `..` (rangeTo) with `until` semantics
  • Believing 5..1 throws instead of being empty

context

open as a page

In Kotlin, how do you check whether a value lies inside a range, and what does the `in` operator do under the hood for a range like `1..10`?

level: juniorimportance: must knowfreq 78%

basics

~10 s

Use the in keyword: x in 1..10 is true when x is between 1 and 10, both ends included. !in checks the opposite. It calls the range's contains method.

open as a page

How do you create a range of integers in Kotlin, and what does the `..` operator produce?

level: juniorimportance: must knowfreq 78%

basics

~10 s

Write 1..5 to make a range from 1 to 5 including both ends. You can then loop over it with for (i in 1..5).

open as a page

How do `step` and `downTo` change the type and iteration of a range? What are first, last, and step after `10 downTo 1 step 3`?

level: middleimportance: must knowfreq 60%

basics

~10 s

step changes how far each jump is, and downTo counts backwards. Together they make a progression that goes down. For 10 downTo 1 step 3 you get 10, 7, 4, 1.

open as a page

How do you build a descending range and how do you change the increment? Explain `downTo` and `step`.

level: middleimportance: must knowfreq 64%

basics

~10 s

Use downTo to count down, like 5 downTo 1. Use step to skip values, like 0..10 step 2 for 0,2,4,6,8,10. You can combine them: 10 downTo 0 step 2.

open as a page

What is the difference between `..` and `..<` / `until`, and when would you reach for the half-open form?

level: middleimportance: must knowfreq 70%

basics

~10 s

.. includes the last value; ..< (and the older until) stops just before it. Use the half-open form for index loops like 0..<list.size so you don't run off the end.

open as a page

How do CharRange and LongRange differ from IntRange, and how does iterating `'a'..'z'` work under the hood?

level: middleimportance: should knowfreq 40%

basics

~10 s

CharRange spans characters like 'a' to 'z', and LongRange spans large whole numbers. They work just like IntRange but for those types, and looping over 'a'..'z' gives each letter in order.

open as a page

How do char ranges like `'a'..'z'` work for character classification, and how would you test if a Char is an ASCII letter or digit using ranges?

level: middleimportance: should knowfreq 64%

basics

~10 s

'a'..'z' is a range of characters in code-point order. c in 'a'..'z' is true for lowercase letters. Combine ranges with || to test letters or digits.

open as a page

The `in` operator works on both ranges and collections (e.g. `x in list`). How do these differ in semantics and performance, and what surprises can `in` produce with strings and reversed ranges?

level: middleimportance: should knowfreq 52%

basics

~20 s

in always calls contains. For a range it's a quick comparison; for a list it scans elements (equals); for a Set it's a fast hash lookup; for a String it checks substrings. Reversed numeric ranges are empty.

open as a page

IntProgression implements Iterable<Int>, yet `for (i in 0..n)` rarely allocates. Explain how iteration works and when the compiler optimization does or doesn't kick in.

level: seniorimportance: should knowfreq 35%

basics

~20 s

A progression knows how to hand out numbers one by one through an iterator. But when you write a simple for-loop over a literal range, the compiler turns it into a plain counting loop with no extra objects, so it's fast.

open as a page

Explain the relationship between the `in` operator and `ClosedRange.contains`. When does `in` dispatch to `ClosedRange.contains` versus a more specialized override, and why does it matter?

level: seniorimportance: should knowfreq 48%

basics

~10 s

x in range calls range.contains(x). ClosedRange provides a default contains using compareTo. Specialized ranges like IntRange override it with faster primitive comparisons.

open as a page

What are the operator-precedence and edge-case pitfalls when writing range expressions like `0..n-1`, `1..count step 2`, or negative steps?

level: seniorimportance: should knowfreq 33%

basics

~20 s

.. binds tighter than +/- is a myth — actually +/- bind tighter, so 0..n-1 works as expected. But .. binds tighter than infix step/downTo calls, and a wrong or negative step throws at runtime, not compile time.

open as a page

What operator functions back `..`, `..<`, `downTo`, and `step`, and what does that imply for using them on your own or non-Int types?

level: seniorimportance: should knowfreq 40%

basics

~10 s

.. calls rangeTo, ..< calls rangeUntil; downTo and step are infix library functions. Because .. and ..< are operators, you can define them on your own Comparable types to support in checks.

open as a page

How are IntProgression equality, emptiness, and the `last` value defined? Why might two differently-written ranges be equal, and when is a progression empty?

level: seniorimportance: nice to knowfreq 22%

basics

~10 s

A progression is empty when it would produce no numbers (start past end for its direction). Two progressions count as equal when they're both empty, or when their start, end, and step all match.

open as a page

How would you enable the `in` operator for membership tests against an interval of your own domain type, and what are the correctness pitfalls (Comparable consistency, half-open intervals, thread safety)?

level: principalimportance: nice to knowfreq 22%

basics

~10 s

Make your type Comparable, then build a ClosedRange (or define your own operator fun contains). After that, value in interval just works. Ensure your ordering is consistent and total.

open as a page