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.
| Function | Object as | Returns | Typical use |
|---|---|---|---|
let | it | lambda result | null checks: user?.let { show(it) } |
run | this | lambda result | compute a result from an object |
with(obj) | this | lambda result | call many methods on one object |
apply | this | the object | configure an object: Intent().apply { putExtra(...) } |
also | it | the object | side 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-localreturn, needed when it is called from another context (e.g. inside aRunnable).reified: in an inline function, keeps the generic type at runtime, soT::classandis Twork. 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.