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 libraries — kotlinx.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:
| Function | Receiver | Returns | Use |
|---|---|---|---|
let | it | block result | operate on a value, possibly null-safe |
run | this | block result | operate as a member of the receiver |
with | this | block result | block-form access to a non-null receiver |
apply | this | the receiver | initialise / configure |
also | it | the receiver | side 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:
letwith?.for null-safe operations.applyfor builder-style initialisation.alsofor logging or side effects in chains.run/withfor 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
associateByfor 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
runCatchingfor fluent error handling. - Use
alsofor 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.