Polyglot
Languages Kotlin stdlib
Kotlin § stdlib

Standard library

The Kotlin standard library admits substantial functionality on top of Java’s: collection extensions (the substantial Iterable/Collection/List/Map operations like map, filter, groupBy), string utilities (split, trim, regex), numeric extensions (range operators, coerceIn, toBigInteger), scoped functions (let, run, with, apply, also), the kotlin.io package (file extensions, BufferedReader.readLines), kotlin.text (regex literals, formatting), kotlin.collections (substantial collection operations). Beyond the standard library, kotlinx librarieskotlinx.coroutines, kotlinx.serialization, kotlinx.datetime, kotlinx.collections.immutable — admit substantial features. Kotlin runs on the JVM so the entire Java standard library is admitted. The combination — substantial Kotlin extensions over Java, the kotlinx ecosystem, the conventional scoped functions, the substantial Sequence support — is the substance of Kotlin’s runtime library.

This tour points out the principal types and functions.

Scoped functions

The Kotlin scoped functions admit substantial fluent patterns:

FunctionReceiverReturnsUse
letitblock resultoperate on a value, possibly null-safe
runthisblock resultoperate as a member of the receiver
withthisblock resultblock-form access to a non-null receiver
applythisthe receiverinitialise / configure
alsoitthe receiverside effects in a chain
// let — operate on non-null value:
val length = nullable?.let { it.length }

// run — block on this:
val s = StringBuilder().run {
    append("Hello, ")
    append("World!")
    toString()
}

// with — block on non-null:
val result = with(builder) {
    append("!")
    toString()
}

// apply — initialise:
val list = mutableListOf<Int>().apply {
    add(1)
    add(2)
    add(3)
}

// also — side effect:
val saved = user.also { repository.save(it) }

The conventional uses:

  • let with ?. for null-safe operations.
  • apply for builder-style initialisation.
  • also for logging or side effects in chains.
  • run/with for accessing receiver members.

Collections

The Kotlin extensions admit substantial functional-style operations:

val list = listOf(1, 2, 3, 4, 5)

// Transformation:
list.map { it * 2 }                                // [2, 4, 6, 8, 10]
list.flatMap { listOf(it, -it) }                   // [1, -1, 2, -2, ...]
list.mapIndexed { i, x -> "[$i] = $x" }
list.mapNotNull { if (it > 0) it.toString() else null }
list.flatten()                                     // for List<List<T>>

// Filtering:
list.filter { it.isEven() }
list.filterNot { it < 0 }
list.filterNotNull()                               // remove nulls
list.filterIsInstance<Int>()                       // by type

// Aggregation:
list.sum()
list.average()
list.min(); list.max()
list.minOrNull(); list.maxOrNull()
list.reduce { a, b -> a + b }
list.fold(0) { acc, x -> acc + x }

// Inspection:
list.contains(3)
list.containsAll(listOf(1, 2))
list.any { it > 4 }
list.all { it > 0 }
list.none { it > 100 }
list.count { it > 2 }
list.first(); list.firstOrNull(); list.firstOrNull { ... }
list.last(); list.lastOrNull()

// Searching:
list.find { it > 2 }
list.indexOf(3)
list.indexOfFirst { it > 2 }
list.indexOfLast { it < 4 }

// Sorting:
list.sorted()
list.sortedDescending()
list.sortedBy { it.toString().length }
list.sortedByDescending { it }
list.sortedWith(compareBy({ it.x }, { it.y }))

// Slicing:
list.take(3)
list.takeLast(2)
list.takeWhile { it < 4 }
list.drop(2)
list.dropLast(1)
list.dropWhile { it < 3 }
list.subList(1, 3)

// Grouping:
list.groupBy { if (it.isEven()) "even" else "odd" }
list.groupingBy { it / 10 * 10 }.eachCount()
list.partition { it > 2 }                          // (matched, unmatched)

// Combining:
list.zip(otherList)
list.zip(otherList) { a, b -> a + b }
list.zipWithNext()                                  // pairs of consecutive

// Distinct:
list.distinct()
list.distinctBy { it.toString().length }

// Reduce-style:
list.runningFold(0) { acc, x -> acc + x }          // [0, 1, 3, 6, 10, 15]
list.runningReduce { a, b -> a + b }
list.scan(0) { acc, x -> acc + x }                 // (alias)

// Conversion:
list.toSet()
list.toMutableList()
list.toIntArray()
list.toMap()                                        // for List<Pair<K, V>>
list.associateBy { it.id }                         // Map<id, item>
list.associate { it to it.toString() }             // Map<item, item.toString()>
list.associateWith { it.toString() }               // Map<item, transform(item)>

// Chunking and windowing:
list.chunked(2)                                    // [[1,2], [3,4], [5]]
list.windowed(3)                                   // [[1,2,3], [2,3,4], [3,4,5]]
list.windowed(3, step = 2)                         // [[1,2,3], [3,4,5]]

Maps

val map = mapOf("a" to 1, "b" to 2)

map["a"]                                           // Int? (nullable)
map.getValue("a")                                  // Int (throws if missing)
map.getOrDefault("c", 0)
map.getOrElse("c") { 0 }

map.keys
map.values
map.entries

map.filter { (_, v) -> v > 0 }
map.filterKeys { it.startsWith("a") }
map.filterValues { it > 1 }

map.mapValues { (_, v) -> v * 2 }
map.mapKeys { (k, _) -> k.uppercase() }
map.mapNotNull { (k, v) -> if (v > 0) k to v.toString() else null }.toMap()

// Iteration:
for ((key, value) in map) {
    println("$key: $value")
}

map.forEach { (k, v) -> println("$k: $v") }

// Mutating:
val mutable = mutableMapOf<String, Int>()
mutable["a"] = 1
mutable.put("b", 2)
mutable.putAll(other)
mutable.remove("a")
mutable.computeIfAbsent("c") { 0 }

Sequences

Sequence<T> admits lazy evaluation:

val result = (1..1_000_000).asSequence()
    .map { it * 2 }
    .filter { it % 3 == 0 }
    .take(10)
    .toList()

For infinite sequences:

val powers = generateSequence(1) { it * 2 }
    .takeWhile { it < 1000 }
    .toList()                                      // [1, 2, 4, ..., 512]

val fibonacci = generateSequence(0 to 1) { (a, b) -> b to (a + b) }
    .map { it.first }
    .take(10)
    .toList()

kotlin.text

import kotlin.text.*

"hello world".uppercase()                          // "HELLO WORLD"
"hello world".capitalize()                         // deprecated; use replaceFirstChar { it.uppercase() }
"hello".replace("l", "L")
"hello".substring(1, 4)
"hello".take(3)                                    // "hel"
"hello".drop(3)                                    // "lo"
"hello".count { it == 'l' }
"hello".reversed()
"  hello  ".trim()
"hello".padStart(10)
"hello".padEnd(10, '*')
"a,b,c".split(",")
listOf("a", "b").joinToString(", ")

Regex

val pattern = Regex("""\d+""")

pattern.containsMatchIn("abc 123")                 // true
pattern.find("abc 123")?.value                     // "123"
pattern.findAll("abc 123 def 456").map { it.value }.toList()  // ["123", "456"]
pattern.replace("abc 123", "X")                    // "abc X"

// Named groups:
val datePattern = Regex("""(?<year>\d{4})-(?<month>\d{2})-(?<day>\d{2})""")
val match = datePattern.find("2026-01-15")
match?.groups?.get("year")?.value                  // "2026"

Numeric

import kotlin.math.*

abs(-5)                                            // 5
max(3, 7)                                          // 7
min(3, 7)                                          // 3

3.14.roundToInt()                                  // 3
3.5.roundToInt()                                   // 4

sqrt(16.0)                                         // 4.0
pow(2.0, 10.0)                                     // 1024.0
log(Math.E)                                        // 1.0
log10(100.0)                                       // 2.0

5.coerceIn(0, 10)                                  // 5
15.coerceIn(0, 10)                                 // 10 (clamped)
(-5).coerceAtLeast(0)                              // 0
15.coerceAtMost(10)                                // 10

kotlinx libraries

The official Kotlin extensions:

kotlinx.coroutines

For substantial async/concurrent work; treated in Coroutines.

kotlinx.serialization

Type-safe serialisation:

import kotlinx.serialization.*
import kotlinx.serialization.json.*

@Serializable
data class User(val id: Int, val name: String, val email: String)

val user = User(1, "Alice", "alice@b.c")
val json = Json.encodeToString(user)
// {"id":1,"name":"Alice","email":"alice@b.c"}

val parsed = Json.decodeFromString<User>(json)

// With config:
val pretty = Json {
    prettyPrint = true
    ignoreUnknownKeys = true
    isLenient = true
}
val prettyJson = pretty.encodeToString(user)

kotlinx.datetime

Date/time handling (cross-platform):

import kotlinx.datetime.*

val now = Clock.System.now()                       // Instant
val today = Clock.System.todayIn(TimeZone.currentSystemDefault())  // LocalDate

val date = LocalDate(2026, 1, 15)
val time = LocalTime(10, 0, 0)
val dateTime = LocalDateTime(date, time)

val tomorrow = date.plus(1, DateTimeUnit.DAY)
val nextMonth = date.plus(1, DateTimeUnit.MONTH)

now.toLocalDateTime(TimeZone.UTC).year

kotlinx.collections.immutable

Immutable persistent collections:

import kotlinx.collections.immutable.*

val list = persistentListOf(1, 2, 3)
val updated = list.add(4)                          // returns new list

val map = persistentMapOf("a" to 1, "b" to 2)
val newMap = map.put("c", 3)

kotlin.io

File and I/O extensions:

import java.io.File

File("data.txt").readText()                        // entire file
File("data.txt").readLines()                       // List<String>
File("data.txt").writeText("hello")
File("data.txt").appendText("more")

File("data.txt").bufferedReader().use { it.readText() }

File("dir").listFiles()
File("dir").walk().filter { it.isFile }

Treated in I/O.

Java standard library

Kotlin admits the entire Java standard library — admit substantial substrate:

// Collections (in addition to Kotlin's):
import java.util.*

val deque = ArrayDeque<Int>()
val tree = TreeMap<String, Int>()
val priority = PriorityQueue<Int>()

// Concurrency:
import java.util.concurrent.*
import java.util.concurrent.atomic.*

val executor = Executors.newFixedThreadPool(10)
val atomic = AtomicInteger(0)
val map = ConcurrentHashMap<String, Int>()

// Date/time (java.time):
import java.time.*

val now = Instant.now()
val today = LocalDate.now()
val time = LocalTime.now()

// Files (java.nio):
import java.nio.file.*

val path = Paths.get("/etc/hosts")
Files.readAllLines(path)
Files.writeString(path, "content")

The conventional Kotlin discipline favours Kotlin extensions where they admit conciseness; substantial Java APIs are reached for when no Kotlin alternative exists.

Common patterns

Counting

val counts = words.groupingBy { it }.eachCount()

// Or:
val counts = words.fold(mutableMapOf<String, Int>()) { acc, w ->
    acc[w] = (acc[w] ?: 0) + 1
    acc
}

Group-by and aggregate

val byCategory = items
    .groupBy { it.category }
    .mapValues { (_, items) -> items.sumOf { it.amount } }

// Or:
val byCategory = items.groupingBy { it.category }
    .fold(0.0) { acc, item -> acc + item.amount }

Top-N

val top10 = items.sortedByDescending { it.score }.take(10)

// More efficient with maxByOrNull (single):
val winner = items.maxByOrNull { it.score }

Filter and transform

val result = users
    .filter { it.isActive }
    .map { it.email }
    .sorted()
    .take(50)

Functional pipeline

val processed = data.asSequence()
    .filter { it.isValid }
    .map { it.normalised() }
    .distinctBy { it.key }
    .take(100)
    .toList()

Building a Map

val byId = users.associateBy { it.id }
val nameToAge = users.associate { it.name to it.age }
val nameToUser = users.associateBy { it.name }

Builder DSL with apply

val config = Config().apply {
    host = "localhost"
    port = 8080
    timeout = 30
}

Null-safe transform

val emailUpper = email?.uppercase()
val length = email?.let { it.length } ?: 0

val processed = input
    ?.trim()
    ?.takeIf { it.isNotEmpty() }
    ?.let { transform(it) }

Fluent chain with also

val user = User("Alice")
    .also { println("created: $it") }
    .also { repository.save(it) }
    .also { notifier.notify(it) }

Ranges and progressions

(1..100).sum()                                     // 5050
(1..10).map { it * it }                            // squares
(1..100).filter { it.isEven() }
('a'..'z').toList()
(1..100 step 5).count()

Read file line-by-line

File("log.txt").useLines { lines ->
    lines.filter { it.contains("ERROR") }
        .forEach { println(it) }
}

Pretty-printed JSON

import kotlinx.serialization.json.Json

val json = Json {
    prettyPrint = true
    encodeDefaults = false
}

val text = json.encodeToString(user)

Date arithmetic

import kotlinx.datetime.*

val today = Clock.System.todayIn(TimeZone.UTC)
val nextWeek = today.plus(7, DateTimeUnit.DAY)
val firstOfNextMonth = LocalDate(today.year, today.monthNumber + 1, 1)

Concurrent map access

import java.util.concurrent.ConcurrentHashMap

val cache = ConcurrentHashMap<String, Data>()

cache.computeIfAbsent("key") { fetchData() }
cache.compute("counter") { _, v -> (v ?: 0) + 1 }

A note on the conventional discipline

The contemporary Kotlin standard-library advice:

  • Use Kotlin extensions over Java equivalents (File.readText(), String.toIntOrNull()).
  • Use scoped functions (apply, let, also) for fluent patterns.
  • Use sequences for substantial chains.
  • Use groupBy/groupingBy.eachCount() for counting.
  • Use associateBy for Map construction from collections.
  • Use kotlinx.coroutines for async work.
  • Use kotlinx.serialization for type-safe serialisation.
  • Use kotlinx.datetime for cross-platform dates.
  • Reach for Java APIs when no Kotlin alternative exists.
  • Use runCatching for fluent error handling.
  • Use also for side effects in chains.

The combination — substantial Kotlin extensions over Java, the conventional scoped functions, the substantial collection method library, the kotlinx libraries, the Java-standard-library substrate — is the substance of Kotlin’s runtime library. The discipline produces concise, expressive code with substantial built-in functionality and substantial cross-platform support.