Skip to main content

Kotlin Collections Overview

Collections are groups of values with the same type that are treated as a single unit. Kotlin provides a rich collection framework that is both powerful and easy to use, taking inspiration from functional programming while maintaining compatibility with Java collections.

Introduction to Kotlin Collections

In Kotlin, collections are fundamental data structures that allow you to store, retrieve, manipulate, and pass around groups of related values. Understanding collections is crucial for effective programming as they form the backbone of data manipulation in most applications.

Kotlin's collection framework has two main characteristics:

  1. Mutable vs Immutable Collections: Kotlin clearly distinguishes between read-only and mutable collections
  2. Rich API: Kotlin provides extensive functions to transform and manipulate collections efficiently

Collection Types

Kotlin supports the following main collection types:

Arrays

Arrays in Kotlin are represented by the Array class, which is a fixed-size collection that stores values of the same type.

kotlin
// Creating arrays
val numbers = arrayOf(1, 2, 3, 4, 5)
val zeros = IntArray(5) // Creates [0, 0, 0, 0, 0]
val squares = Array(5) { i -> i * i } // Creates [0, 1, 4, 9, 16]

// Accessing elements
println(numbers[0]) // Output: 1

// Modifying elements
numbers[0] = 10
println(numbers[0]) // Output: 10

// Array properties
println("Size: ${numbers.size}") // Output: Size: 5

Lists

Lists are ordered collections where elements can be accessed by indices. Kotlin provides both read-only (List) and mutable (MutableList) versions.

kotlin
// Read-only list
val readOnlyList = listOf("Apple", "Banana", "Cherry")

// Mutable list
val mutableList = mutableListOf("Apple", "Banana", "Cherry")
mutableList.add("Date")
mutableList.removeAt(0)

println(readOnlyList) // Output: [Apple, Banana, Cherry]
println(mutableList) // Output: [Banana, Cherry, Date]

// Common operations
println("First element: ${readOnlyList.first()}") // Output: First element: Apple
println("Last element: ${readOnlyList.last()}") // Output: Last element: Cherry
println("List size: ${readOnlyList.size}") // Output: List size: 3
println("Contains Banana? ${readOnlyList.contains("Banana")}") // Output: Contains Banana? true

Sets

Sets are collections of unique elements. Like lists, Kotlin provides both read-only (Set) and mutable (MutableSet) versions.

kotlin
// Read-only set
val readOnlySet = setOf("Apple", "Banana", "Cherry", "Apple") // Note the duplicate "Apple"

// Mutable set
val mutableSet = mutableSetOf("Apple", "Banana", "Cherry")
mutableSet.add("Date")
mutableSet.remove("Apple")

println(readOnlySet) // Output: [Apple, Banana, Cherry] - duplicates are removed
println(mutableSet) // Output: [Banana, Cherry, Date]

// Common operations
println("Set size: ${readOnlySet.size}") // Output: Set size: 3
println("Contains Cherry? ${readOnlySet.contains("Cherry")}") // Output: Contains Cherry? true

Maps

Maps store key-value pairs. Kotlin provides both read-only (Map) and mutable (MutableMap) versions.

kotlin
// Read-only map
val readOnlyMap = mapOf(
"apple" to 100,
"banana" to 150,
"cherry" to 200
)

// Mutable map
val mutableMap = mutableMapOf(
"apple" to 100,
"banana" to 150,
"cherry" to 200
)
mutableMap["date"] = 250
mutableMap.remove("apple")

println(readOnlyMap) // Output: {apple=100, banana=150, cherry=200}
println(mutableMap) // Output: {banana=150, cherry=200, date=250}

// Accessing values
println("Price of banana: ${readOnlyMap["banana"]}") // Output: Price of banana: 150

// Common operations
println("Map size: ${readOnlyMap.size}") // Output: Map size: 3
println("Contains key 'apple'? ${readOnlyMap.containsKey("apple")}") // Output: Contains key 'apple'? true
println("Contains value 200? ${readOnlyMap.containsValue(200)}") // Output: Contains value 200? true

Mutable vs Immutable Collections

One of Kotlin's key features is the clear distinction between mutable and immutable collections:

Immutable (Read-only)Mutable
List<T>MutableList<T>
Set<T>MutableSet<T>
Map<K, V>MutableMap<K, V>
kotlin
// Read-only (immutable) collections
val immutableList = listOf(1, 2, 3)
val immutableSet = setOf(1, 2, 3)
val immutableMap = mapOf("a" to 1, "b" to 2)

// Mutable collections
val mutableList = mutableListOf(1, 2, 3)
val mutableSet = mutableSetOf(1, 2, 3)
val mutableMap = mutableMapOf("a" to 1, "b" to 2)

// This works
mutableList.add(4)

// This would cause a compilation error
// immutableList.add(4)

Collection Constructors

Kotlin provides several ways to create collections:

kotlin
// Empty collections
val emptyList = emptyList<String>()
val emptySet = emptySet<Int>()
val emptyMap = emptyMap<String, Int>()

// Collections with elements
val list = listOf(1, 2, 3)
val set = setOf("a", "b", "c")
val map = mapOf(1 to "one", 2 to "two")

// Mutable collections
val mutableList = mutableListOf<String>()
mutableList.add("Hello")

val mutableSet = mutableSetOf<Int>()
mutableSet.add(42)

val mutableMap = mutableMapOf<String, Int>()
mutableMap["answer"] = 42

Practical Examples

Example 1: Managing a To-Do List

kotlin
fun main() {
val todoList = mutableListOf(
"Buy groceries",
"Pay bills",
"Call mom",
"Finish Kotlin tutorial"
)

// Display current tasks
println("Current tasks:")
todoList.forEachIndexed { index, task ->
println("${index + 1}. $task")
}

// Mark a task as completed (remove it)
val completedTask = todoList.removeAt(0)
println("\nCompleted: $completedTask")

// Add new task
todoList.add("Go to the gym")

// Display updated list
println("\nUpdated tasks:")
todoList.forEachIndexed { index, task ->
println("${index + 1}. $task")
}
}

Output:

Current tasks:
1. Buy groceries
2. Pay bills
3. Call mom
4. Finish Kotlin tutorial

Completed: Buy groceries

Updated tasks:
1. Pay bills
2. Call mom
3. Finish Kotlin tutorial
4. Go to the gym

Example 2: Frequency Analysis of Words

kotlin
fun main() {
val text = """
Kotlin is a modern programming language.
Kotlin runs on the JVM.
Kotlin is concise and expressive.
""".trimIndent()

// Split into words and convert to lowercase
val words = text.lowercase().split(Regex("\\s+|\\.|,"))
.filter { it.isNotEmpty() }

// Count word frequencies
val wordFrequency = mutableMapOf<String, Int>()

for (word in words) {
wordFrequency[word] = wordFrequency.getOrDefault(word, 0) + 1
}

// Display results
println("Word Frequency Analysis:")
wordFrequency.entries
.sortedByDescending { it.value }
.forEach { (word, count) ->
println("$word: $count")
}
}

Output:

Word Frequency Analysis:
kotlin: 3
is: 2
a: 1
modern: 1
programming: 1
language: 1
runs: 1
on: 1
the: 1
jvm: 1
concise: 1
and: 1
expressive: 1

Example 3: Student Grade Tracker

kotlin
data class Student(val name: String, val id: Int)

fun main() {
// Map of students and their grades
val grades = mutableMapOf<Student, MutableList<Int>>()

// Add students and grades
val alice = Student("Alice", 101)
val bob = Student("Bob", 102)
val charlie = Student("Charlie", 103)

grades[alice] = mutableListOf(85, 90, 78, 92)
grades[bob] = mutableListOf(76, 88, 90, 84)
grades[charlie] = mutableListOf(92, 93, 88, 95)

// Calculate average grade for each student
val averages = grades.mapValues { (_, gradeList) ->
gradeList.average()
}

// Print results
println("Student Grade Averages:")
averages.forEach { (student, average) ->
println("${student.name} (ID: ${student.id}): ${"%.2f".format(average)}")
}

// Find highest achieving student
val topStudent = averages.maxByOrNull { it.value }
topStudent?.let {
println("\nHighest achieving student: ${it.key.name} with average ${it.value}")
}
}

Output:

Student Grade Averages:
Alice (ID: 101): 86.25
Bob (ID: 102): 84.50
Charlie (ID: 103): 92.00

Highest achieving student: Charlie with average 92.0

Collection Operations

Kotlin collections come with a rich set of operations influenced by functional programming:

Transformation Operations

kotlin
val numbers = listOf(1, 2, 3, 4, 5)

// map - transform each element
val doubled = numbers.map { it * 2 }
println(doubled) // Output: [2, 4, 6, 8, 10]

// filter - keep elements matching predicate
val evens = numbers.filter { it % 2 == 0 }
println(evens) // Output: [2, 4]

// flatMap - map each element to a collection and flatten the results
val nested = listOf(listOf(1, 2, 3), listOf(4, 5, 6))
val flattened = nested.flatMap { it }
println(flattened) // Output: [1, 2, 3, 4, 5, 6]

Grouping Operations

kotlin
val words = listOf("apple", "banana", "cherry", "avocado", "blueberry")

// Group by first letter
val grouped = words.groupBy { it.first() }
println(grouped)
// Output: {a=[apple, avocado], b=[banana, blueberry], c=[cherry]}

// Partition into two groups
val (shortWords, longWords) = words.partition { it.length <= 5 }
println("Short words: $shortWords") // Output: Short words: [apple]
println("Long words: $longWords") // Output: Long words: [banana, cherry, avocado, blueberry]

Aggregation Operations

kotlin
val numbers = listOf(1, 2, 3, 4, 5)

// Sum
println(numbers.sum()) // Output: 15

// Average
println(numbers.average()) // Output: 3.0

// Min and Max
println(numbers.minOrNull()) // Output: 1
println(numbers.maxOrNull()) // Output: 5

// Reduce - combine elements into a single value
val product = numbers.reduce { acc, i -> acc * i }
println(product) // Output: 120 (1*2*3*4*5)

// Fold - like reduce but with an initial value
val sumPlusTen = numbers.fold(10) { acc, i -> acc + i }
println(sumPlusTen) // Output: 25 (10+1+2+3+4+5)

Summary

Kotlin collections provide a powerful and flexible way to work with groups of data. The key points to remember:

  • Kotlin provides both mutable and immutable (read-only) collections
  • Main collection types are Lists, Sets, Maps, and Arrays
  • Collections come with a rich API influenced by functional programming
  • Kotlin collections are fully interoperable with Java collection classes

Mastering collections is essential for effective Kotlin programming, as they enable you to handle complex data manipulations with clear and concise code.

Exercises

  1. Create a program that takes a list of numbers and returns a new list containing only the even numbers, doubled.
  2. Write a function that takes a list of strings and returns a map where the keys are the lengths of the strings and the values are lists of strings with that length.
  3. Create a program that manages a shopping cart using a mutable map, allowing items to be added, removed, and quantities to be updated.
  4. Write a function that takes two lists and returns a set containing only elements that appear in both lists.
  5. Create a program that reads a text file and counts the frequency of each word, ignoring case.

Additional Resources

Happy coding with Kotlin collections!



If you spot any mistakes on this website, please let me know at [email protected]. I’d greatly appreciate your feedback! :)