Contents

Collections

In Kotlin, collections are a key feature used to store and manage groups of data. Kotlin offers several types of collections, such as Lists, Sets, Maps, Arrays, and Sequences, each with distinct characteristics for handling data efficiently.

Types of Kotlin Collections

1. List: An ordered collection that allows duplicate elements. Elements can be accessed by their index.
2. Set: An unordered collection that holds unique elements, ensuring no duplicates.
3. Map: A collection of key-value pairs where each key is unique, but values can be duplicated.
4. Array: A fixed-size collection of elements of the same type.
5. Sequence: A lazily evaluated collection that can be processed step-by-step.

Example of Using a List in Kotlin:

				
					val cities = listOf("New York", "London", "Paris", "Tokyo")

// Access elements
println("First city: ${cities[0]}")
println("Last city: ${cities.last()}")

// Iterate over the list
for (city in cities) {
    println(city)
}

// Filter the list
val filteredCities = cities.filter { it.startsWith("L") }
println("Filtered cities: $filteredCities")

				
			

Output:

				
					First city: New York
Last city: Tokyo
New York
London
Paris
Tokyo
Filtered cities: [London]

				
			

Explanation: In this example, we create an immutable list of fruits, access elements, iterate over the list, and filter elements that start with the letter “a”.

ArrayList

Explanation: In this example, we create an immutable list of fruits, access elements, iterate over the list, and filter elements that start with the letter “a”.

Types of Collections in Kotlin

1. Immutable Collections : Immutable collections are read-only and cannot be modified after creation. Some common functions include:

  • listOf(), setOf(), and mapOf().

Example of Immutable List:

				
					val immutableList = listOf("John", "Sarah", "Emma")
for (person in immutableList) {
    println(person)
}

				
			

Output:

				
					John
Sarah
Emma

				
			

Example of Immutable Set:

				
					val immutableSet = setOf(3, 5, 3, "Alice", "Bob")
for (item in immutableSet) {
    println(item)
}

				
			

Output:

				
					3
5
Alice
Bob

				
			

Example of Immutable Map:

				
					val immutableMap = mapOf(1 to "Alice", 2 to "Bob", 3 to "Charlie")
for (key in immutableMap.keys) {
    println(immutableMap[key])
}

				
			

Output:

				
					Alice
Bob
Charlie

				
			

Key Characteristics of Immutable Collections:

  • Immutable lists, sets, and maps are fixed, and elements cannot be added, removed, or modified.

2. Mutable Collections : Mutable collections support both read and write operations, allowing modification of the collection. Common methods include:

  • mutableListOf(), mutableSetOf(), mutableMapOf().

Example of Mutable List:

				
					val mutableList = mutableListOf("John", "Sarah", "Emma")
mutableList[1] = "Mike"
mutableList.add("Lisa")
for (person in mutableList) {
    println(person)
}

				
			

Output:

				
					John
Mike
Emma
Lisa

				
			

Example of Mutable Set:

				
					val mutableSet = mutableSetOf(8, 12)
mutableSet.add(5)
mutableSet.add(10)
for (item in mutableSet) {
    println(item)
}
				
			

Output:

				
					8
12
5
10

				
			

Example of Mutable Map:

				
					val mutableMap = mutableMapOf(1 to "Alice", 2 to "Bob", 3 to "Charlie")
mutableMap[1] = "Dave"
mutableMap[4] = "Eve"
for (value in mutableMap.values) {
    println(value)
}

				
			

Output:

				
					Dave
Bob
Charlie
Eve
				
			
Advantages of Kotlin Collections:
  • Improved Readability: The use of collections makes code more readable by abstracting complex operations on data.

  • Efficient Memory Management: Collections handle memory automatically, reducing the need for manual intervention.

  • Efficient Data Operations: Kotlin’s collections are designed to work efficiently with large datasets.

  • Type Safety: Collections enforce type safety, ensuring that only the correct type of data can be stored.

Disadvantages of Kotlin Collections:
  • Performance Overhead: Using collections can add overhead, particularly when compared to working directly with primitive types or arrays.

  • Increased Memory Usage: Larger collections may consume more memory than necessary for simple operations.

  • Additional Complexity: In some cases, collections may add unnecessary complexity when performing advanced operations on data.

The ArrayList class in Kotlin is a dynamic array that allows you to increase or decrease its size as needed. It provides both read and write operations and allows duplicate elements. Since it is not synchronized, ArrayList is not thread-safe, but it is efficient for single-threaded operations.

Key Features of ArrayList:
  • Dynamic resizing: The array size grows or shrinks as required.

  • Read/write functionality: You can modify elements after creation.

  • Non-synchronized: It is not thread-safe, but efficient in single-threaded environments.

  • Duplicate elements: It allows duplicates, unlike Set.

  • Index-based access: You can access and manipulate elements via their index.

Constructors

1. ArrayList<E>(): Creates an empty ArrayList.
2. ArrayList(capacity: Int): Creates an ArrayList of specified capacity.
3. ArrayList(elements: Collection<E>): Fills an ArrayList with elements from a collection.

Important Methods

1. add(index: Int, element: E): Inserts an element at the specified index.

Example:

				
					fun main() {
    val arrayList = ArrayList<String>()
    arrayList.add("Lion")
    arrayList.add("Tiger")

    println("Array list --->")
    for (item in arrayList) println(item)

    arrayList.add(1, "Elephant")
    println("Array list after insertion --->")
    for (item in arrayList) println(item)
}

				
			

Output:

				
					Array list --->
Lion
Tiger
Array list after insertion --->
Lion
Elephant
Tiger

				
			

2. addAll(index: Int, elements: Collection<E>) : Adds all elements from a specified collection to the current list.

Example:

				
					fun main() {
    val arrayList = ArrayList<String>()
    arrayList.add("Red")
    arrayList.add("Blue")

    val additionalColors = arrayListOf("Green", "Yellow")

    println("Original ArrayList -->")
    for (item in arrayList) println(item)

    arrayList.addAll(1, additionalColors)
    println("ArrayList after adding new colors -->")
    for (item in arrayList) println(item)
}

				
			

Output:

				
					Original ArrayList -->
Red
Blue
ArrayList after adding new colors -->
Red
Green
Yellow
Blue

				
			

3. get(index: Int): Returns the element at the specified index.

Example:

				
					fun main() {
    val arrayList = ArrayList<Int>()
    arrayList.add(100)
    arrayList.add(200)
    arrayList.add(300)

    println("All elements:")
    for (item in arrayList) print("$item ")
    
    println("\nElement at index 1: ${arrayList.get(1)}")
}

				
			

Output:

				
					All elements:
100 200 300 
Element at index 1: 200
				
			

4. set(index: Int, element: E):Replaces the element at the specified index with the given element.

Example:

				
					fun main() {
    val arrayList = ArrayList<String>()
    arrayList.add("Dog")
    arrayList.add("Cat")
    arrayList.add("Rabbit")

    println("Before update:")
    for (item in arrayList) println(item)

    arrayList.set(2, "Horse")
    println("After update:")
    for (item in arrayList) println(item)
}

				
			

Output:

				
					Before update:
Dog
Cat
Rabbit
After update:
Dog
Cat
Horse

				
			

5. indexOf(element: E): Returns the index of the first occurrence of the specified element, or -1 if the element is not found.

Example:

				
					fun main() {
    val arrayList = ArrayList<String>()
    arrayList.add("Apple")
    arrayList.add("Banana")
    arrayList.add("Apple")
    
    for (item in arrayList) print("$item ")
    println("\nFirst occurrence of 'Apple': ${arrayList.indexOf("Apple")}")
}

				
			

Output:

				
					Apple Banana Apple 
First occurrence of 'Apple': 0
				
			

6. remove(element: E): Removes the first occurrence of the specified element.

Example:

				
					fun main() {
    val arrayList = ArrayList<String>()
    arrayList.add("Car")
    arrayList.add("Bike")
    arrayList.add("Bus")

    println("Before removal:")
    for (item in arrayList) println(item)

    arrayList.remove("Bike")
    println("After removal:")
    for (item in arrayList) println(item)
}
				
			

Output:

				
					Before removal:
Car
Bike
Bus
After removal:
Car
Bus

				
			

7. clear():Removes all elements from the ArrayList.

Example:

				
					fun main() {
    val arrayList = ArrayList<Int>()
    arrayList.add(1)
    arrayList.add(2)
    arrayList.add(3)
    arrayList.add(4)

    println("Before clearing:")
    for (item in arrayList) print("$item ")
    
    arrayList.clear()
    println("\nArrayList size after clear: ${arrayList.size}")
}

				
			

Output:

				
					Before clearing:
1 2 3 4 
ArrayList size after clear: 0

				
			

Full Example Using ArrayList:

				
					fun main() {
    val list = arrayListOf(5, 10, 15)

    println("Initial list: $list")

    // Add elements to the list
    list.add(20)
    list.add(1, 25) // Adds 25 at index 1

    println("After adding elements: $list")

    // Remove elements from the list
    list.remove(10)
    list.removeAt(0) // Removes element at index 0

    println("After removing elements: $list")

    // Update elements in the list
    list[1] = 30

    println("After updating elements: $list")

    // Access elements in the list
    val first = list[0]
    val last = list.last()

    println("First element: $first")
    println("Last element: $last")

    // Iterate over the list
    for (element in list) {
        println(element)
    }
}

				
			

Output:

				
					Initial list: [5, 10, 15]
After adding elements: [5, 25, 10, 15, 20]
After removing elements: [25, 15, 20]
After updating elements: [25, 30, 20]
First element: 25
Last element: 20
25
30
20

				
			

listOf()

In Kotlin, listOf() is used to create immutable lists, meaning once the list is created, its elements cannot be modified. The listOf() function takes a variable number of arguments and returns a new list containing those elements.

Example:

				
					val numbers = listOf(10, 20, 30, 40, 50)

				
			

In this example, the list numbers is immutable, and contains five integers. Since the list is immutable, you cannot change its contents once it is created.

Accessing Elements in a List:

Elements can be accessed using indices.

Example:

				
					val firstNumber = numbers[0]  // returns 10
val lastNumber = numbers[numbers.size - 1]  // returns 50
				
			

In this example, we use indexing to access the first and last elements of the list. Note that indexing starts at 0.

Iterating Over a List:

You can iterate over the list using a for loop or other iteration methods.

Example:

				
					for (number in numbers) {
    println(number)
}

				
			

This will print each element of the numbers list to the console.

Advantages of Using listOf() in Kotlin:

1. Immutability: Once created, the list cannot be modified, which makes it safer for multi-threaded environments.
2. Type Safety: Since Kotlin is statically typed, the elements of the list will always be of the same type.
3. Convenience: You can easily create a list with multiple elements using a single function.

Disadvantages of Using listOf():

1. Immutability: If you need to modify the list during runtime, you should use mutableListOf() instead.
2. Performance Overhead: Since immutable lists cannot be modified, operations that modify the list (like adding or removing elements) require creating new lists, which can be inefficient.
3. Limited Functionality: listOf() does not support operations like adding or removing elements. For these, you need a mutable list.

Kotlin Program with listOf() Containing Integers:

				
					val set = setOf("Apple", "Banana", "Cherry", "Apple")
println(set)  // Output: [Apple, Banana, Cherry]

// set.add("Date")  // Error: Cannot add element to immutable set
				
			

Output:

				
					List size: 3
Index of 22: 1
Element at index 2: 33

				
			
Indexing Elements of a List:

In Kotlin, elements of a list are indexed starting from 0.

Example:

				
					fun main() {
    val numbers = listOf(5, 10, 15, 20, 25, 30, 35)

    val firstElement = numbers[0]
    println("First element: $firstElement")

    val seventhElement = numbers[6]
    println("Element at index 6: $seventhElement")

    val firstIndex = numbers.indexOf(5)
    println("Index of first occurrence of 5: $firstIndex")

    val lastIndex = numbers.lastIndexOf(5)
    println("Last index of 5: $lastIndex")

    val lastIndexOverall = numbers.lastIndex
    println("Last index of the list: $lastIndexOverall")
}

				
			

Output:

				
					First element: 5
Element at index 6: 35
Index of first occurrence of 5: 0
Last index of 5: 0
Last index of the list: 6

				
			
Retrieving First and Last Elements:

You can retrieve the first and last elements of the list without using the get() method.

Example:

				
					fun main() {
    val numbers = listOf(3, 6, 9, 12, 15)
    println("First element: ${numbers.first()}")
    println("Last element: ${numbers.last()}")
}

				
			

Output:

				
					First element: 3
Last element: 15

				
			
List Iteration Methods:

Kotlin provides several ways to iterate over lists.

Example:

				
					fun main() {
    val fruits = listOf("Apple", "Banana", "Cherry", "Date", "Elderberry")

    // Method 1: Simple for loop
    for (fruit in fruits) {
        print("$fruit, ")
    }
    println()

    // Method 2: Using index
    for (i in fruits.indices) {
        print("${fruits[i]} ")
    }
    println()

    // Method 3: Using forEachIndexed
    fruits.forEachIndexed { index, fruit -> println("fruits[$index] = $fruit") }

    // Method 4: Using ListIterator
    val iterator: ListIterator<String> = fruits.listIterator()
    while (iterator.hasNext()) {
        val fruit = iterator.next()
        print("$fruit ")
    }
    println()
}

				
			

Output:

				
					Apple, Banana, Cherry, Date, Elderberry, 
Apple Banana Cherry Date Elderberry 
fruits[0] = Apple
fruits[1] = Banana
fruits[2] = Cherry
fruits[3] = Date
fruits[4] = Elderberry
Apple Banana Cherry Date Elderberry 

				
			
Sorting Elements in a List:

You can sort the elements in ascending or descending order using sorted() and sortedDescending().

Example:

				
					fun main() {
    val numbers = listOf(42, 12, 7, 99, 23, 1)

    val ascendingOrder = numbers.sorted()
    println("Sorted in ascending order: $ascendingOrder")

    val descendingOrder = numbers.sortedDescending()
    println("Sorted in descending order: $descendingOrder")
}

				
			

Output:

				
					Sorted in ascending order: [1, 7, 12, 23, 42, 99]
Sorted in descending order: [99, 42, 23, 12, 7, 1]

				
			
contains() and containsAll() Functions:

These methods check if a list contains certain elements.

Example:

				
					fun main() {
    val numbers = listOf(3, 5, 7, 9, 11)

    val containsSeven = numbers.contains(7)
    if (containsSeven) {
        println("The list contains 7")
    } else {
        println("The list does not contain 7")
    }

    val containsThreeAndTen = numbers.containsAll(listOf(3, 10))
    if (containsThreeAndTen) {
        println("The list contains 3 and 10")
    } else {
        println("The list does not contain 3 and 10")
    }
}

				
			

Output:

				
					The list contains 7
The list does not contain 3 and 10

				
			
Kotlin Set Interface

The Set interface in Kotlin represents an unordered collection of unique elements. Sets are particularly useful when you need to store data without duplicates. Kotlin supports two types of sets:

1. Immutable Sets: These are created using setOf() and only support read-only operations.
2. Mutable Sets: These are created using mutableSetOf() and support both read and write operations.

Syntax for setOf():

				
					fun <T> setOf(vararg elements: T): Set<T>

				
			

1. Immutable Set Using setOf(): This function creates an immutable set that can hold any number of elements of the same type, and it returns a set that does not contain duplicate elements.

Example:

				
					fun main() {
    val setA = setOf("Kotlin", "Java", "Python")
    val setB = setOf('A', 'B', 'C')
    val setC = setOf(10, 20, 30, 40)

    // Traverse through the sets
    for (item in setA) print(item + " ")
    println()
    for (item in setB) print(item + " ")
    println()
    for (item in setC) print(item.toString() + " ")
}

				
			

Output:

				
					Kotlin Java Python
A B C
10 20 30 40

				
			

2. Mutable Sets in Kotlin (using mutableSetOf()): A mutable set is a collection that allows modifications after it is created. You can add, remove, or update elements in a mutable set. This is particularly useful when you need a set with dynamic content.

Example:

				
					val colors = mutableSetOf("Red", "Green", "Blue")

// Adding elements
colors.add("Yellow")
println(colors) // Output: [Red, Green, Blue, Yellow]

// Removing elements
colors.remove("Green")
println(colors) // Output: [Red, Blue, Yellow]

// Checking for an element
println("Contains Blue? ${"Blue" in colors}") // Output: Contains Blue? true

// Adding duplicate elements
colors.add("Red") // Ignored, as "Red" already exists
println(colors) // Output: [Red, Blue, Yellow]

				
			
Set Indexing

You can access elements at specific indexes in a set using functions like elementAt(), indexOf(), and lastIndexOf().

Example: Using Index in Set

				
					fun main() {
    val players = setOf("Virat", "Smith", "Root", "Kane", "Rohit")

    println("Element at index 2: " + players.elementAt(2))
    println("Index of 'Smith': " + players.indexOf("Smith"))
    println("Last index of 'Rohit': " + players.lastIndexOf("Rohit"))
}

				
			

Output:

				
					Element at index 2: Root
Index of 'Smith': 1
Last index of 'Rohit': 4

				
			
Set First and Last Elements

You can retrieve the first and last elements using first() and last() functions.

Example: First and Last Element in Set

				
					fun main() {
    val elements = setOf(1, 2, 3, "A", "B", "C")

    println("First element: " + elements.first())
    println("Last element: " + elements.last())
}

				
			

Output:

				
					First element: 1
Last element: C
				
			
Set Basic Functions

Basic functions like count(), max(), min(), sum(), and average() help perform arithmetic operations on sets of numbers.

Example: Basic Functions with Set

				
					fun main() {
    val numbers = setOf(5, 10, 15, 20, 25)

    println("Count of elements: " + numbers.count())
    println("Maximum element: " + numbers.maxOrNull())
    println("Minimum element: " + numbers.minOrNull())
    println("Sum of elements: " + numbers.sum())
    println("Average of elements: " + numbers.average())
}

				
			

Output:

				
					Count of elements: 5
Maximum element: 25
Minimum element: 5
Sum of elements: 75
Average of elements: 15.0

				
			
Checking Elements with contains() and containsAll()

The contains() function checks if a set contains a specific element, and containsAll() checks if a set contains all specified elements.

Example: Using contains() and containsAll()

				
					fun main() {
    val captains = setOf("Kohli", "Smith", "Root", "Rohit")

    val element = "Rohit"
    println("Does the set contain $element? " + captains.contains(element))

    val elementsToCheck = setOf("Kohli", "Smith")
    println("Does the set contain all elements? " + captains.containsAll(elementsToCheck))
}

				
			

Output:

				
					Does the set contain Rohit? true
Does the set contain all elements? true

				
			

hashSetOf()

hashSetOf creates a mutable set of unique elements backed by a hash table.

Example:

				
					fun main() {
    val emptySetA = setOf<String>()
    val emptySetB = setOf<Int>()

    println("Is setA empty? " + emptySetA.isEmpty())
    println("Are both sets equal? " + (emptySetA == emptySetB))
}

				
			

Output:

				
					Is setA empty? true
Are both sets equal? true

				
			

Example: Set Operations Using setOf()

				
					fun main() {
    val fruits = setOf("Apple", "Banana", "Cherry")

    val containsApple = fruits.contains("Apple")
    val containsOrange = fruits.contains("Orange")

    println("Fruits: $fruits")
    println("Contains Apple: $containsApple")
    println("Contains Orange: $containsOrange")

    for (fruit in fruits) {
        println(fruit)
    }
}

				
			

Output:

				
					Fruits: [Apple, Banana, Cherry]
Contains Apple: true
Contains Orange: false
Apple
Banana
Cherry

				
			
Advantages of setOf() in Kotlin:
  • Immutability: The contents cannot be modified once created, making the set safe for use in multithreaded environments.

  • No Duplicates: The set automatically ensures that no duplicates are present, which reduces bugs.

  • Convenience: Creating a set with setOf() is simple and concise.

Disadvantages of setOf() in Kotlin:
  • Immutability: You cannot modify the set after its creation, which may limit its use in situations where you need to add or remove elements dynamically.

  • No Advanced Features: For more advanced operations (like adding or removing elements), you will need to use a mutable set via mutableSetOf().

mapOf()

In Kotlin, a Map is a collection that stores data as key-value pairs. Each pair associates a unique key with a corresponding value, and the keys in a map must be distinct. A map can hold only one value per key.

Kotlin distinguishes between immutable maps, which are created using mapOf(), and mutable maps, created using mutableMapOf(). An immutable map is read-only, while a mutable map allows both reading and writing.

Syntax:

				
					fun <K, V> mapOf(vararg pairs: Pair<K, V>): Map<K, V>

				
			

In each pair, the first value represents the key, and the second value represents the corresponding value. If multiple pairs have the same key, the map will store only the last value associated with that key. The entries in the map are traversed in the specified order.

Example: Using mapOf()

				
					fun main() {
    // Creating a map of integer to string
    val map = mapOf(10 to "Apple", 20 to "Banana", 30 to "Cherry")
    // Printing the map
    println(map)
}

				
			

Output:

				
					{10=Apple, 20=Banana, 30=Cherry}

				
			

Accessing Map Keys, Values, and Entries

				
					fun main() {
    // Creating a map of integers to strings
    val fruits = mapOf(1 to "Mango", 2 to "Orange", 3 to "Peach", 4 to "Grape")

    println("Map Entries: $fruits")
    println("Map Keys: ${fruits.keys}")
    println("Map Values: ${fruits.values}")
}

				
			

Output:

				
					Map Entries: {1=Mango, 2=Orange, 3=Peach, 4=Grape}
Map Keys: [1, 2, 3, 4]
Map Values: [Mango, Orange, Peach, Grape]

				
			
Determining the Map Size

You can determine the size of a map using either the size property or the count() method:

				
					fun main() {
    val countries = mapOf(1 to "USA", 2 to "Canada", 3 to "Japan", 4 to "Germany")
    
    // Method 1
    println("The size of the map is: ${countries.size}")
    
    // Method 2
    println("The size of the map is: ${countries.count()}")
}

				
			

Output:

				
					The size of the map is: 4
The size of the map is: 4

				
			
Creating an Empty Map

You can create an empty map using mapOf():

				
					fun main() {
    // Creating an empty map
    val emptyMap = mapOf<String, Int>()
    
    println("Entries: ${emptyMap.entries}")
    println("Keys: ${emptyMap.keys}")
    println("Values: ${emptyMap.values}")
}

				
			

Output:

				
					Entries: []
Keys: []
Values: []

				
			
Retrieving Values from a Map

Here are several ways to retrieve values from a map:

				
					fun main() {
    val capitals = mapOf(1 to "Tokyo", 2 to "Paris", 3 to "London", 4 to "Berlin")

    // Method 1
    println("The capital at rank #1 is: ${capitals[1]}")
    
    // Method 2
    println("The capital at rank #3 is: ${capitals.getValue(3)}")
    
    // Method 3
    println("The capital at rank #4 is: ${capitals.getOrDefault(4, "Unknown")}")
    
    // Method 4
    val city = capitals.getOrElse(2) { "No City" }
    println(city)
}

				
			

Output:

				
					The capital at rank #1 is: Tokyo
The capital at rank #3 is: London
The capital at rank #4 is: Berlin
Paris
				
			
Checking if a Map Contains a Key or Value

You can check if a map contains a specific key or value using the containsKey() and containsValue() methods:

				
					fun main() {
    val numbers = mapOf("one" to 1, "two" to 2, "three" to 3)

    val key = "two"
    if (numbers.containsKey(key)) {
        println("Yes, the map contains key $key")
    } else {
        println("No, it doesn't contain key $key")
    }

    val value = 4
    if (numbers.containsValue(value)) {
        println("Yes, the map contains value $value")
    } else {
        println("No, it doesn't contain value $value")
    }
}

				
			

Output:

				
					Yes, the map contains key two
No, it doesn't contain value 4

				
			
Handling Duplicate Keys

If two pairs have the same key, the map will retain only the last value:

				
					fun main() {
    // Two pairs with the same key
    val map = mapOf(1 to "Alpha", 2 to "Beta", 1 to "Gamma")
    println("Entries in the map: ${map.entries}")
}

				
			

Output:

				
					Entries in the map: [1=Gamma, 2=Beta]
				
			
Advantages of mapOf():
  • Provides a straightforward way to create a key-value mapping.

  • Read-only maps are safe for use across functions and threads without the risk of concurrent modification.

Disadvantages of mapOf():
  • Immutable, meaning the map cannot be altered after creation. For modifiable maps, mutableMapOf() should be used.

  • May become inefficient with a large number of entries, as lookup times can degrade. For more frequent lookups, a data structure like a hash table or binary tree may be more appropriate.

HashMap

A Kotlin HashMap is a collection that holds key-value pairs, implemented using a hash table. It implements the MutableMap interface, meaning that you can modify its contents. Each key within a HashMap is unique, and the map will store only one value per key. The HashMap is declared as HashMap<key, value> or HashMap<K, V>. This hash table-based implementation does not guarantee the order of the keys, values, or entries in the map.

Constructors of Kotlin HashMap Class:

Kotlin provides four constructors for the HashMap class, all of which have a public access modifier:

  • HashMap(): The default constructor, which creates an empty HashMap.
  • HashMap(initialCapacity: Int, loadFactor: Float = 0f): This constructor initializes a HashMap with a specified capacity and an optional load factor. If neither is provided, the default values are used.
  • HashMap(initialCapacity: Int): Constructs a HashMap with the specified initial capacity. If the capacity isn’t used, it is ignored.
  • HashMap(original: Map<out K, V>): Creates a new HashMap instance that contains the same mappings as the provided map.
				
					fun main(args: Array<String>) {
    // Define an empty HashMap with <String, Int>
    val heroesMap: HashMap<String, Int> = HashMap()

    // Printing the empty HashMap
    printMap(heroesMap)

    // Adding elements to the HashMap using put() function
    heroesMap["CaptainAmerica"] = 1940
    heroesMap["Hulk"] = 5000
    heroesMap["BlackWidow"] = 1000
    heroesMap["DoctorStrange"] = 1500
    heroesMap["AntMan"] = 700

    // Printing the non-empty HashMap
    printMap(heroesMap)

    // Using Kotlin's print function to get the same result
    println("heroesMap: $heroesMap\n")

    // Traversing the HashMap using a for loop
    for (key in heroesMap.keys) {
        println("Element at key $key : ${heroesMap[key]}")
    }

    // Creating another HashMap with the previous heroesMap object
    val copyOfHeroesMap: HashMap<String, Int> = HashMap(heroesMap)

    println("\nCopy of HeroesMap:")
    for (key in copyOfHeroesMap.keys) {
        println("Element at key $key : ${copyOfHeroesMap[key]}")
    }

    // Clearing the HashMap
    println("heroesMap.clear()")
    heroesMap.clear()

    println("After Clearing: $heroesMap")
}

// Function to print the HashMap
fun printMap(map: HashMap<String, Int>) {
    if (map.isEmpty()) {
        println("The HashMap is empty")
    } else {
        println("HashMap: $map")
    }
}

				
			

Output:

				
					val hashMap = HashMap<Int, String>()
hashMap[1] = "Apple"
hashMap[2] = "Banana"
hashMap[3] = "Cherry"

println(hashMap)  // Output: {1=Apple, 2=Banana, 3=Cherry}

hashMap[4] = "Date"
println(hashMap)  // Output: {1=Apple, 2=Banana, 3=Cherry, 4=Date}

hashMap.remove(2)
println(hashMap)  // Output: {1=Apple, 3=Cherry, 4=Date}

				
			
Explanation of Changes:

1. Updated Key-Value Pairs: Changed the keys to other superhero names like “CaptainAmerica”, “Hulk”, etc., and assigned different values.
2. Renamed Variables: Renamed the hashMap variable to heroesMap for better readability, and secondHashMap to copyOfHeroesMap.
3. Simplified Code: Improved readability and kept the method calls the same while modifying variable names and values.

Output:

				
					The HashMap is empty

HashMap: {CaptainAmerica=1940, Hulk=5000, BlackWidow=1000, DoctorStrange=1500, AntMan=700}
heroesMap: {CaptainAmerica=1940, Hulk=5000, BlackWidow=1000, DoctorStrange=1500, AntMan=700}

Element at key CaptainAmerica : 1940
Element at key Hulk : 5000
Element at key BlackWidow : 1000
Element at key DoctorStrange : 1500
Element at key AntMan : 700

Copy of HeroesMap:
Element at key CaptainAmerica : 1940
Element at key Hulk : 5000
Element at key BlackWidow : 1000
Element at key DoctorStrange : 1500
Element at key AntMan : 700

heroesMap.clear()
After Clearing: {}

				
			

Kotlin Program Using HashMap Functions (get(), replace(), put())

				
					fun main(args: Array<String>) {
    // Create an empty HashMap with <String, Int>
    val heroesMap: HashMap<String, Int> = HashMap()

    // Adding elements to the HashMap using put() function
    heroesMap.put("Batman", 5000)
    heroesMap.put("Superman", 2000)
    heroesMap.put("WonderWoman", 3000)
    heroesMap.put("Flash", 1500)

    // Printing the initial HashMap
    for (key in heroesMap.keys) {
        println("Element at key $key : ${heroesMap[key]}")
    }

    // Accessing elements using hashMap[key]
    println("\nheroesMap[\"Batman\"]: ${heroesMap["Batman"]}")

    // Updating a value using put() function
    heroesMap["Superman"] = 4000
    println("heroesMap.get(\"Superman\"): ${heroesMap.get("Superman")}\n")

    // Replacing values using replace() and put()
    heroesMap.replace("Flash", 1800)
    heroesMap.put("Superman", 4000)

    println("After replacing 'Flash' and updating 'Superman':")

    // Printing the updated HashMap
    for (key in heroesMap.keys) {
        println("Element at key $key : ${heroesMap[key]}")
    }
}

				
			

Output:

				
					Element at key Batman : 5000
Element at key Superman : 2000
Element at key WonderWoman : 3000
Element at key Flash : 1500

heroesMap["Batman"]: 5000
heroesMap.get("Superman"): 4000

After replacing 'Flash' and updating 'Superman':
Element at key Batman : 5000
Element at key Superman : 4000
Element at key WonderWoman : 3000
Element at key Flash : 1800

				
			
Advantages of HashMap:
  • HashMap offers a versatile way to store key-value pairs, making it simple and convenient to use.

  • It provides highly efficient O(1) time complexity for common operations like adding, removing, and retrieving elements.

  • HashMap is capable of storing various types of data, including custom objects, making it adaptable to different scenarios.

Disadvantages of HashMap:
  • HashMap tends to consume more memory than other data structures because it holds both keys and values.

  • By default, HashMap is not thread-safe, meaning concurrent access without proper synchronization can lead to data corruption or unexpected behavior. For multi-threaded access, a thread-safe alternative or synchronization mechanisms should be implemented.

  • HashMap does not guarantee the order of elements, which can be a drawback if the order of entries is important. In such cases, you may want to consider using a different structure like LinkedHashMap that maintains the order of insertion.