Why did Kotlin "take away" our add, remove, and put? Let's figure it out once and for all
\n\nIf you've ever written in Java and then switched to Kotlin, you've probably encountered this moment of slight bewilderment. You create a List, want to add an element to it — you write .add(), and the IDE highlights it in red. Or you try to put a value into a Map using .put() — and again an error.
Sound familiar? You bet! The question "Kotlin's List missing "add", "remove", Map missing "put", etc?" on Stack Overflow has gathered thousands of views and dozens of comments. It's a real stumbling block for beginners, and even experienced developers sometimes catch themselves thinking: "Where are my usual methods?"
\n\nIn this article, we won't just answer this question. We'll show how Kotlin's philosophy turns this "shortcoming" into your main advantage. You'll learn best practices for working with collections in 2025, learn to choose the right collection type, and stop confusing listOf and mutableListOf. Let's go!
What's the deal? Immutability as a way of life
\n\nThe first thing to understand: Kotlin was designed from the start as a language that encourages safe and predictable code. One of the main principles is immutability by default.
\n\nIn Java, you have one List interface, and it always contains add(), remove(), set() methods, and so on. Even if you create a list via List.of() (Java 9+), you still have access to these methods — they just throw an exception at runtime. This is inconvenient and unsafe.
Kotlin took a different path. It divided collections into two types:
\n\n- \n
- Immutable (read-only):
List,Set,Map— they have no methods that modify the content. \n - Mutable:
MutableList,MutableSet,MutableMap— these have all the familiaradd,remove,put. \n
That's exactly why listOf(1, 2, 3) does not have an .add() method. It returns an immutable list. And mutableListOf(1, 2, 3) returns a mutable one, and it does have .add().
Sounds logical? Let's look at the code.
\n\nExamples: what works and what doesn't
\n\nHere's a classic situation that confuses beginners:
\n\n// ❌ Error! List has no add()\nval fruits = listOf("apple", "banana")\nfruits.add("orange") // Compilation error\n\n// ✅ Correct: use MutableList\nval mutableFruits = mutableListOf("apple", "banana")\nmutableFruits.add("orange") // Now it works!\nprintln(mutableFruits) // [apple, banana, orange]\n\n\nThe same story with Map:
\n\n// ❌ Error! Map has no put()\nval capitals = mapOf("Russia" to "Moscow", "France" to "Paris")\ncapitals.put("Germany", "Berlin") // Compilation error\n\n// ✅ Correct: use MutableMap\nval mutableCapitals = mutableMapOf("Russia" to "Moscow", "France" to "Paris")\nmutableCapitals.put("Germany", "Berlin")\n// Or even shorter:\nmutableCapitals["Germany"] = "Berlin"\nprintln(mutableCapitals) // {Russia=Moscow, France=Paris, Germany=Berlin}\n\n\nNote: for MutableMap you can use the operator brackets [] — this is put() under the hood. Very convenient!
But why? Why does Kotlin make life so complicated?
\n\nAt first glance, it seems like an unnecessary headache. Why remember two types of collections? In fact, this is a brilliant solution. Here are three main reasons:
\n\n1. Type-level safety
\nThe compiler now ensures you don't accidentally modify a list that should be