Step 1 of 5 · Reading · ~3 min
Learn
Collections
Maps and Sets
A List answers "what is at position 3?". The two collections in this lesson answer different
questions. A Map answers "what is stored under this key?", and a Set answers "have I
seen this one before?".
Map: keys to values
fun main() {
val ages = mapOf("Ada" to 36, "Grace" to 45)
println(ages["Ada"])
println(ages["Alan"])
println(ages.size)
println(ages.containsKey("Grace"))
}
//> 36
//> null
//> 2
//> true
to is not a keyword. It is an ordinary function written in infix position: "Ada" to 36 builds
a Pair, and mapOf collects those pairs into a map.
What happens when the key is missing?
Look at the second line of that output again. ages["Alan"] did not throw and did not quietly
return 0 — it returned null. That is deliberate. The index operator on a Map<String, Int>
gives back an Int?, so the compiler forces you to decide what a missing key means before you
can use the value.
| Written | Result for a missing key |
|---|---|
ages["Alan"] | null |
ages["Alan"] ?: 0 | 0 |
ages.getOrElse("Alan") { 0 } | 0 |
ages["Alan"]!! | throws NullPointerException |
✓ Counting with a fallback — the standard Kotlin idiom:
fun main() {
val counts = mutableMapOf<String, Int>()
for (word in listOf("red", "blue", "red")) {
counts[word] = (counts[word] ?: 0) + 1
}
println(counts)
}
//> {red=2, blue=1}
✗ The same loop written with !! crashes on the very first word, because that key is not in the
map yet. The Elvis operator is what turns "not there" into "start at zero".
Walking a map
fun main() {
val ages = mapOf("Ada" to 36, "Grace" to 45)
for ((name, age) in ages) {
println(name + " is " + age)
}
}
//> Ada is 36
//> Grace is 45
mapOf keeps insertion order, so that output is stable rather than lucky. keys and values
hand you each side of the map on its own.
Set: membership without duplicates
A Set stores each element at most once. Adding something that is already there is simply
ignored.
fun main() {
val tags = setOf("kotlin", "jvm", "kotlin")
println(tags)
println(tags.size)
println("jvm" in tags)
}
//> [kotlin, jvm]
//> 2
//> true
Membership is the reason sets exist. Checking in on a List walks the whole list, which is
, while a hash-based Set answers in on average. Across three items that is
nothing; across three hundred thousand it is the difference between instant and hopeless.
Counting distinct things
fun main() {
val words = listOf("the", "cat", "the", "dog")
println(words.toSet())
println(words.toSet().size)
println(words.distinct())
println(words.size)
}
//> [the, cat, dog]
//> 3
//> [the, cat, dog]
//> 4
.toSet() gives back a Set; .distinct() gives back a List with first-seen order preserved.
Both compare elements with equals, so they work out of the box for strings, numbers, and data
classes.
Which one do I want?
| Question you are asking | Collection |
|---|---|
| What is the third item? | List |
| Have I already seen this? | Set |
| What value belongs to this key? | Map |
| How many different values are there? | Set, via .toSet().size |
Each has a mutable twin — mutableListOf, mutableSetOf, mutableMapOf — and the rule from
the Lists and Arrays lesson still holds: use the read-only form until you actually need to change
the contents.
Your exercise
Distinct Words reads one line of space-separated words and prints how many different words appeared.
The starter splits the line into a List<String> for you. The grader's first visible test is
the quick brown the — four words, three distinct — and it expects 3.
Two mistakes it catches. Printing words.size gives 4, because that counts the repeat.
Printing the collection instead of its size outputs [the, quick, brown] rather than a number.
Print a single integer and nothing else.
Discussion
Ask a question, share an insight, or help someone who’s stuck.
Sign in to post a comment or reply.
Loading…