Welcome to our Kotlin data structures revision! Today, we will delve deeply into Kotlin Maps. Just like a library card catalog, maps enable you to quickly locate the information you need by referring to a label (key). They are vital in Kotlin for efficiently accessing values using keys, as well as for key insertion and deletion. Let's explore Kotlin's Map
and MutableMap
for a clearer understanding of these concepts.
Maps in Kotlin are a type of data structure that hold data as key-value pairs. They provide an efficient way to store and retrieve information based on unique keys. Each key in a map is associated with exactly one value. You can think of a map like a dictionary where you look up a word (key) to get its definition (value). Keys are unique, but values don't have to be.
Maps can be either mutable or immutable in Kotlin. Immutable maps are read-only, meaning you cannot add, remove, or modify entries once they're created. Mutable maps allow for dynamic changes, such as adding, updating, and removing key-value pairs, making them highly flexible for situations where the dataset needs to be manipulated frequently.
Here's a small example of an immutable map:
Kotlin1val countryCodes: Map<String, String> = mapOf("US" to "United States", "CA" to "Canada") 2 3fun main(){ 4 println(countryCodes["US"]) // Output: "United States" 5 println(countryCodes["CA"]) // Output: "Canada" 6 7 // Since this is a read-only map, you cannot add, remove, or modify entries. 8 }
Now, let's look at an example of a PhoneBook
class using a MutableMap
to store contacts, allowing for dynamic changes:
Kotlin1class PhoneBook { 2 3 private val contacts: MutableMap<String, String> = mutableMapOf() 4 5 fun addContact(name: String, phoneNumber: String) { 6 contacts[name] = phoneNumber 7 } 8 9 fun getPhoneNumber(name: String): String? { 10 return contacts[name] 11 } 12 13 fun hasContact(name: String): Boolean { 14 return contacts.containsKey(name) 15 } 16 17 fun showContact(name: String): String { 18 return contacts.get(name) ?: "Contact not found" 19 } 20} 21 22 23fun main() { 24 // Create a PhoneBook instance 25 val phoneBook = PhoneBook() 26 27 // Add contacts 28 phoneBook.addContact("Alice", "123-456-7890") 29 phoneBook.addContact("Bob", "234-567-8901") 30 31 // Check if contacts exist 32 println(phoneBook.hasContact("Alice")) // Output: true 33 println(phoneBook.hasContact("Charlie")) // Output: false 34 35 // Retrieve phone numbers 36 println(phoneBook.showContact("Alice")) // Output: "123-456-7890" 37 println(phoneBook.showContact("Charlie")) // Output: "Contact not found" 38}
Explanation
-
private val contacts: MutableMap<String, String>
: This declares aMutableMap
to store contact names (keys) and their corresponding phone numbers (values). As a mutable map, it allows for adding, updating, and removing entries. -
addContact(name: String, phoneNumber: String)
:- Adds or updates a contact in the
contacts
map. - If a contact with the given name already exists, their phone number is updated. Otherwise, a new key-value pair is added.
- Adds or updates a contact in the
-
getPhoneNumber(name: String): String?
:- Retrieves the phone number for a given contact name.
- If the contact is not found, it returns
null
.
-
hasContact(name: String): Boolean
:- Uses
containsKey
to check if the contact name exists in thecontacts
map. - Returns
true
if the contact exists, otherwisefalse
.
- Uses
-
showContact(name: String): String
:- Uses the
get
method to retrieve a contact's phone number. - Leverages the Elvis operator to return "Contact not found" if the contact does not exist.
- Uses the
Kotlin's MutableMap
provides a variety of operations for manipulating data, such as setting, getting, and deleting key-value pairs. Understanding these operations is crucial for efficient data handling in Kotlin.
To add or update entries in a MutableMap
, you directly assign a value to a key. If the key exists, the value is updated; if not, a new key-value pair is added, allowing dynamic updates and additions to the map.
The get operation is used to retrieve the value associated with a specific key. It offers a straightforward way to access values, returning null
if the key does not exist.
Deleting an entry is done using the remove
function followed by the key. This operation removes the specified key-value pair from the MutableMap
, which is essential for actively managing the contents.
Let's see how these operations work in the context of a Task Manager class:
Kotlin1class TaskManager { 2 3 private val tasks: MutableMap<String, String> = mutableMapOf() 4 5 fun addOrUpdateTask(taskName: String, status: String) { 6 tasks[taskName] = status 7 } 8 9 fun getTaskStatus(taskName: String): String { 10 return tasks[taskName] ?: "Not Found" 11 } 12 13 fun deleteTask(taskName: String) { 14 if (tasks.remove(taskName) == null) { 15 println("Task '$taskName' not found.") 16 } 17 } 18} 19 20fun main(){ 21 // Test the TaskManager class 22 val myTasks = TaskManager() 23 myTasks.addOrUpdateTask("Buy Milk", "Pending") 24 println(myTasks.getTaskStatus("Buy Milk")) // Output: "Pending" 25 myTasks.addOrUpdateTask("Buy Milk", "Completed") 26 println(myTasks.getTaskStatus("Buy Milk")) // Output: "Completed" 27 28 myTasks.deleteTask("Buy Milk") 29 println(myTasks.getTaskStatus("Buy Milk")) // Output: "Not Found" 30}
This example showcases how to leverage map operations in Kotlin to effectively manage data by adding, updating, retrieving, and deleting entries through a simulated Task Manager application.
Kotlin provides an elegant way to loop through maps using a for
loop. You can iterate through keys, values, or both simultaneously.
Let's explore this in our Task Manager example:
Kotlin1class TaskManager { 2 private val tasks: MutableMap<String, String> = mutableMapOf() 3 4 fun addTask(taskName: String, status: String) { 5 tasks[taskName] = status 6 } 7 8 fun printAllTasks() { 9 // Prints all tasks' keys 10 for (taskName in tasks.keys) { 11 println(taskName) 12 } 13 } 14} 15 16fun main(){ 17 val myTasks = TaskManager() 18 myTasks.addTask("Buy Milk", "Pending") 19 myTasks.addTask("Pay Bills", "Completed") 20 21 myTasks.printAllTasks() 22}
In this case, iterating over tasks.keys
provides all keys in our map, allowing us to print all tasks in our task manager. You can also use tasks.values
to get all values in the map and tasks.entries
to get all items as key-value pairs.
Nesting in maps involves storing maps within another map. It's useful when associating multiple pieces of information with a key. Let's see how this works in a Student Database example. Here, we will iterate through map entries for displaying students and their subject grades.
Kotlin1class StudentDatabase { 2 private val students: MutableMap<String, MutableMap<String, String>> = mutableMapOf() 3 4 fun addStudent(name: String, subjects: MutableMap<String, String>) { 5 students[name] = subjects 6 } 7 8 fun getMark(name: String, subject: String): String { 9 return students[name]?.get(subject) ?: "N/A" 10 } 11 12 fun printDatabase() { 13 // Prints student and their subjects with grades 14 for ((name, subjects) in students) { 15 println("Student: $name") 16 for ((subject, grade) in subjects) { 17 println(" Subject: $subject, Grade: $grade") 18 } 19 } 20 } 21} 22 23fun main(){ 24 // Create a StudentDatabase instance 25 val studentDb = StudentDatabase() 26 studentDb.addStudent("Alice", mutableMapOf("Math" to "A", "English" to "B")) 27 28 println(studentDb.getMark("Alice", "English")) // Output: "B" 29 println(studentDb.getMark("Alice", "History")) // Output: "N/A" 30 studentDb.printDatabase() 31 /* 32 Output: 33 Student: Alice 34 Subject: Math, Grade: A 35 Subject: English, Grade: B 36 */ 37}
Let's shift our focus to a more interactive and familiar scenario: managing a shopping cart in an online store. This hands-on example will demonstrate how maps can be used to map product names to their quantities in a shopping cart. You will learn how to add products, update quantities, and retrieve the total number of items in the cart.
Here’s how you can implement and manipulate a shopping cart using a Kotlin MutableMap
:
Kotlin1class ShoppingCart { 2 3 private val cart: MutableMap<String, Int> = mutableMapOf() 4 5 fun addProduct(productName: String, quantity: Int) { 6 // Add or update the quantity of a product in the cart 7 cart[productName] = cart.getOrDefault(productName, 0) + quantity 8 } 9 10 fun removeProduct(productName: String) { 11 // Remove a product from the cart 12 if (cart.remove(productName) == null) { 13 println("$productName not found in your cart.") 14 } 15 } 16 17 fun showCart() { 18 // Display the products and their quantities in the cart 19 if (cart.isEmpty()) { 20 println("Your shopping cart is empty.") 21 } else { 22 for ((product, quantity) in cart) { 23 println("$product: $quantity") 24 } 25 } 26 } 27} 28 29fun main(){ 30 // Create an instance of ShoppingCart 31 val myCart = ShoppingCart() 32 33 // Add products and update their quantities 34 myCart.addProduct("Apples", 5) 35 myCart.addProduct("Bananas", 2) 36 myCart.addProduct("Apples", 3) // Updates quantity of apples to 8 37 38 // Display cart 39 myCart.showCart() 40 /* 41 Output: 42 Apples: 8 43 Bananas: 2 44 */ 45 46 // Remove a product and show the updated cart 47 myCart.removeProduct("Bananas") 48 myCart.showCart() 49 /* 50 Output: 51 Apples: 8 52 */ 53}
In the ShoppingCart
class, the getOrDefault
function is used in the addProduct
method to handle the addition or updating of products in the cart. Here's how it works:
Kotlin1fun addProduct(productName: String, quantity: Int) { 2 // Add or update the quantity of a product in the cart 3 cart[productName] = cart.getOrDefault(productName, 0) + quantity 4}
In this line of code, cart.getOrDefault(productName, 0)
is used to retrieve the current quantity of a product in the cart. If the product is not already present in the cart, getOrDefault
will return the specified default value, which is 0
in this case.
This ensures that when you add a product that is not already in the cart, it starts with a quantity of zero, and then the specified quantity is added to it. If the product already exists, its current quantity is retrieved and the new quantity is added to it. This effectively updates the quantity in the cart, ensuring that all additions account for any existing amount of the product.
This example showcases the practical application of maps to manage a dynamic dataset, such as an online shopping cart. By using product names as keys and their quantities as values, we achieve efficient and flexible data manipulation.
Well done! Today, we delved into Kotlin's Map
and MutableMap
and explored various operations on maps. We now invite you to get hands-on experience with the upcoming practice exercises. To master these concepts and hone your Kotlin map skills, practice is key. Happy learning!