Collections and Functional

What is the difference between List and MutableList? Is List immutable?

List is a read-only interface: it has no add/remove. It is not guaranteed immutable, because the same object may be a MutableList (or a Java ArrayList) that someone else changes. Expose List from classes and keep the MutableList private. For true immutability use kotlinx.collections.immutable (persistentListOf), which also helps Compose stability.

Explain map, filter, flatMap, fold and reduce.

Example

reduce starts from the first element and fails on an empty collection; fold takes an initial value (which can be a different type) and works on empty collections.

data class Order(val id: Int, val items: List<String>, val amount: Int)

fun main() {
    val orders = listOf(
        Order(1, listOf("Pen", "Book"), 250),
        Order(2, listOf("Bag"), 900),
        Order(3, listOf("Pen", "Lamp"), 450),
    )
    println(orders.filter { it.amount > 300 }.map { it.id })
    println(orders.flatMap { it.items }.distinct())
    println(orders.map { it.amount }.reduce { acc, x -> acc + x })
    println(orders.fold("Ids:") { acc, o -> "$acc ${o.id}" })
    println(emptyList<Int>().fold(0) { acc, x -> acc + x })
}

Output

[2, 3]
[Pen, Book, Bag, Lamp]
1600
Ids: 1 2 3
0

What is the difference between groupBy, associateBy and partition?

Example

groupBy builds Map<K, List<V>>; associateBy builds Map<K, V> (last one wins on duplicate keys); partition splits into a Pair of matching and non-matching lists.

data class Emp(val name: String, val dept: String, val salary: Int)

fun main() {
    val emps = listOf(Emp("Asha", "Android", 90), Emp("Ravi", "Backend", 80), Emp("Neha", "Android", 70))
    println(emps.groupBy({ it.dept }, { it.name }))
    println(emps.associateBy { it.name }.mapValues { it.value.salary })
    val (high, low) = emps.partition { it.salary >= 80 }
    println("high=${high.map { it.name }} low=${low.map { it.name }}")
}

Output

{Android=[Asha, Neha], Backend=[Ravi]}
{Asha=90, Ravi=80, Neha=70}
high=[Asha, Ravi] low=[Neha]

What is the difference between a Sequence and a collection (Iterable)?

Example

Collection operations are eager: each step creates a new list. Sequence operations are lazy: each element goes through the whole chain one by one, and work stops as soon as a terminal operation (first, toList) has its answer. Use sequences for large data or long chains with early exit.

fun main() {
    val list = listOf(1, 2, 3, 4, 5, 6)
    println("List (eager):")
    val a = list.map { println("  map $it"); it * 2 }.filter { println("  filter $it"); it > 4 }.first()
    println("result $a")
    println("Sequence (lazy):")
    val b = list.asSequence().map { println("  map $it"); it * 2 }.filter { println("  filter $it"); it > 4 }.first()
    println("result $b")
}

Output

List (eager):
  map 1
  map 2
  map 3
  map 4
  map 5
  map 6
  filter 2
  filter 4
  filter 6
  filter 8
  filter 10
  filter 12
result 6
Sequence (lazy):
  map 1
  filter 2
  map 2
  filter 4
  map 3
  filter 6
result 6

Explain the scope functions let, run, with, apply and also.

FunctionObject asReturnsTypical use
letitlambda resultnull checks: user?.let { show(it) }
runthislambda resultcompute a result from an object
with(obj)thislambda resultcall many methods on one object
applythisthe objectconfigure an object: Intent().apply { putExtra(...) }
alsoitthe objectside effects such as logging

What is a higher-order function? Why mark it inline?

A higher-order function takes a function as a parameter or returns one (fun retry(times: Int, block: () -> Unit)). Each lambda normally becomes an object allocation. inline copies the function body and the lambda into the call site, which removes that overhead and allows return from the lambda to return from the enclosing function (non-local return). Use it for small functions that take lambdas, like filter, let and measureTimeMillis.

What do noinline, crossinline and reified mean?

  • noinline: this lambda parameter of an inline function is not inlined, so it can be stored or passed to another function.
  • crossinline: the lambda is inlined but must not do a non-local return, needed when it is called from another context (e.g. inside a Runnable).
  • reified: in an inline function, keeps the generic type at runtime, so T::class and is T work. Example: inline fun <reified T> Gson.fromJson(json: String) = fromJson(json, T::class.java).

What do out and in mean in generics?

out T (covariance): the type only produces T, so List<String> is a List<Any> (like Java ? extends). in T (contravariance): the type only consumes T, so a Comparator<Any> can be used as a Comparator<String> (like ? super). Kotlin declares this once at the class (declaration-site variance) instead of at every use.