Part of Github Copilot with Kotlin

Kotlin max value in the list

Sandy LaneSandy Lane

Video: Kotlin max value in the list by Taught by Celeste AI - AI Coding Coach

Take the quiz on the full lesson page
Test what you've read · interactive walkthrough

Kotlin: Max Value in a List

numbers.maxOrNull() returns the largest element or null for empty lists. The null-safe successor to the deprecated max(). Copilot's go-to one-liner for "find the biggest."

A one-liner with a small but important wrinkle — what happens when the list is empty?

The Copilot prompt

fun main() {
  val numbers = listOf(3, 7, 2, 9, 4)
  // Find the maximum value in the list

Copilot generates:

val maxValue = numbers.maxOrNull()

if (maxValue != null) {
  println("The maximum value is $maxValue")
} else {
  println("The list is empty")
}

maxOrNull vs max

Kotlin used to have max(), which threw NoSuchElementException on empty lists. It was deprecated in Kotlin 1.4 and replaced by maxOrNull().

val empty = emptyList<Int>()
println(empty.maxOrNull())   // null
// println(empty.max())       // would throw if it still existed

maxOrNull() is null-safe by design. The compiler forces you to handle the empty case explicitly — usually via !! (if you're sure), ?: (with a default), or ?.let { ... }.

Common patterns

// Default to 0 if empty
val max = numbers.maxOrNull() ?: 0

// Crash on empty (you're sure it's not)
val max = numbers.maxOrNull()!!

// Branch on empty
numbers.maxOrNull()?.let { println("Max: $it") } ?: println("Empty")

The Elvis operator ?: is the cleanest for "default value." Use !! only when the list is guaranteed non-empty by construction.

Max by a property

For complex objects, use maxByOrNull:

data class Person(val name: String, val age: Int)

val people = listOf(
  Person("Alice", 30),
  Person("Bob", 25),
  Person("Charlie", 35),
)

val oldest = people.maxByOrNull { it.age }
println(oldest)   // Person(name=Charlie, age=35)

maxByOrNull returns the element with the maximum key, not the key itself. To get just the max age:

val maxAge = people.maxOfOrNull { it.age }   // 35 (the Int)
val oldest = people.maxByOrNull { it.age }    // Person(...) (the whole object)

Both have null-safe variants and maxBy/maxOf non-null counterparts that throw on empty.

Min, sum, average — same shape

Kotlin's collection extension functions mirror each other:

numbers.maxOrNull()        // largest
numbers.minOrNull()        // smallest
numbers.sum()              // sum (no null-safe variant; sum of empty is 0)
numbers.average()          // mean as Double (NaN for empty)

people.maxByOrNull { it.age }     // element with max age
people.minByOrNull { it.age }     // element with min age
people.sumOf { it.age }            // total age

sumOf { selector } is the modern variant; the older sumBy and sumByDouble were deprecated in favor of the type-inferred sumOf.

Finding all elements at the max

val all_oldest = people.filter { it.age == people.maxOf { it.age } }

If multiple people share the max age, maxByOrNull returns the first one. filter returns all of them.

For just the value count:

val maxAge = people.maxOf { it.age }
val countAtMax = people.count { it.age == maxAge }

Performance

maxOrNull walks the list once, O(n). For sorted lists, the max is just list.last() — O(1). Use whichever matches your data.

For repeatedly querying the max of a changing list, consider a heap (PriorityQueue) or sorted structure. Pure List doesn't optimize for repeated max-queries.

Common mistakes

Using max() (deprecated). Compiler warning, but works. Always migrate to maxOrNull() when you see it.

Force-unwrapping on user input. userInput.split(",").map { it.toInt() }.maxOrNull()!! crashes if input is empty. Use ?: with a default.

Confusing maxBy and maxOf. maxBy returns the element; maxOf returns the key. Both null-safe variants maxByOrNull and maxOfOrNull.

maxOf { ... } without thinking about types. maxOf { it.age.toDouble() } returns Double, not Int. Pay attention to the inferred return.

Calling maxOrNull() on a Sequence with side effects. Sequences are lazy; maxOrNull triggers iteration. If your sequence has expensive computations, evaluate them once.

What's next

Episode 10: Merge two lists in Kotlin. The + operator works on lists too — and it's the cleanest approach.

Recap

numbers.maxOrNull() for the max (or null if empty). Use ?: for a default, !! only when guaranteed non-empty. maxByOrNull { selector } for "max by property" (returns element); maxOfOrNull { selector } returns the key. Same shape for min*, sum*, average. Walks the list once — O(n).

Next episode: merging two lists.

Ready? Take the quiz on the full lesson page →
Test what you've learned. Watch the lesson and try the interactive quiz on the same page.