8 minutes read

Working with collections, we make use of grouped sets of elements or items, which is essential in a variety of tasks. In previous topics, you have learned to select collection elements, order them, retrieve some of them, and even group or transform them according to your needs.

In this topic, we will focus on single elements and explore different methods Kotlin offers for working with them.

Retrieve by position

Collections, such as lists or sets, are accessed via indices, which start at 0 (the first element) and go up to size-1 (the last element of the collection).

To get the element at a specific index position, you can use elementAt(). We can also fetch an element in an equivalent way with the get() function.

However, in the Kotlin world, things are easier if we use the indexed access operator [], which is equivalent to the get() function on lists.

All of them will throw an IndexOutOfBoundsException if the index is not within the indicated limits (0.. size-1).

fun main() {
    
    val numberList = listOf(1, 2, 3, 4, 5, 6, 7, 8, 9, 10)
    val numberSet = setOf(1, 2, 3, 4, 5, 6, 7, 8, 9, 10)

    // elementAt() returns the element at the specified index
    println(numberList.elementAt(3)) // 4
    println(numberSet.elementAt(3)) // 4

    // get() returns the element at the specified index
    println(numberList.get(3)) // 4
    // println(numberSet.get(3)) // error: 'get' is not a member of 'Set'

    // [] returns the element at the specified index
    println(numberList[3]) // 4
    // println(numberSet[3]) // error: 'get' is not a member of 'Set'

    // be careful with the index – it can throw an exception if the index is out of bounds
    // println(numberList[30]) // error: Index 30 out of bounds for length 10
    // println(numberSet.elementAt(30)) // error: Index 30 out of bounds for length 10

}

Some of the most common operations include getting the first (index = 0) and the last (index = size-1) item in a collection. To avoid mistakes with the indices, we can use first() and last().

fun main() {
    
    val numberList = listOf(1, 2, 3, 4, 5, 6, 7, 8, 9, 10)
    val numberSet = setOf(1, 2, 3, 4, 5, 6, 7, 8, 9, 10)
    
    // first() returns the first element
    println(numberList.first()) // 1
    println(numberSet.first()) // 1
    
    // last() returns the last element
    println(numberList.last()) // 10
    println(numberSet.last()) // 10 

}

In order not to have problems with exceptions when trying to get an invalid index element, we can make use of the following safe calls:

  • elementAtOrNull(): it will return null when the index position is out of the collection bounds.

  • elementAtOrElse(): it takes a lambda function that returns the result of the lambda if the index position is out of the collection bounds.

  • getOrNull(): it is the equivalent to elementAtOrNull() for a list.

  • getOrElse(): it is the equivalent to elementAtOrElse() for a list.

  • firstOrNull(): it is used to get the first element or null if the collection is empty.

  • lastOrNull(): it is used to get the last element or null if the collection is empty.

fun main() {
   
    val emptyList = listOf<Int>()
    val emptySet = setOf<Int>()

    // firstOrNull() returns the first element or null if the list is empty
    println(emptyList.firstOrNull()) // null
    println(emptySet.firstOrNull()) // null

    // lastOrNull() returns the last element or null if the list is empty
    println(emptyList.lastOrNull()) // null
    println(emptySet.lastOrNull()) // null

    // elementAtOrNull() returns the element at the specified index or null if the index is out of bounds
    println(numberList.elementAtOrNull(30)) // null
    println(numberSet.elementAtOrNull(30)) // null

    // getOrNull() returns the element at the specified index or null if the index is out of bounds
    println(numberList.getOrNull(30)) // null
    // println(numberSet.getOrNull(30)) // error: 'getOrNull' is not a member of 'Set'

    // elementAtOrElse() returns the element at the specified index
    // or the result of the given function if the index is out of bounds
    println(numberList.elementAtOrElse(30) { "element not found" }) // element not found
    println(numberSet.elementAtOrElse(30) { "element not found" }) // element not found

    // getOrElse() returns the element at the specified index
    // or the result of the given function if the index is out of bounds
    println(numberList.getOrElse(30) { "element not found" }) // element not found
    // println(numberSet.getOrElse(30) { "element not found" }) // error: 'getOrElse' is not a member of 'Set'
}

Retrieve by condition

Some functions allow you to get elements based on a predicate: first() and last(). You will only get the element if the predicate is true. To avoid an exception, you can use the secure calls: firstOrNull() and lastOrNull().
For the sake of ease, you can use these handy aliases:

  • find(): it is used instead of firstOrNull() and returns the first collection element according to a predicate or null if it does not exist.

  • findLast(): it is used instead of lastOrNull() and returns the last collection element according to a predicate or null if it does not exist.

fun main() {
    
    val numberList = listOf(1, 2, 3, 4, 5, 6, 7, 8, 9, 10)
    val numberSet = setOf(1, 2, 3, 4, 5, 6, 7, 8, 9, 10)

    // first() with a lambda predicate
    println(numberList.first { it > 5 }) // 6
    println(numberSet.first { it > 5 }) // 6
    
    // last() with a lambda predicate
    println(numberList.last { it > 5 }) // 10
    println(numberSet.last { it > 5 }) // 10
    
    // be careful with the predicate – it can throw an exception if the predicate is not satisfied
    // println(numberList.first { it > 50 }) // error: NoSuchElementException
    // println(numberSet.first { it > 50 }) // error: NoSuchElementException
    
    // println(numberList.last { it > 50 }) // error: NoSuchElementException
    // println(numberSet.last { it > 50 }) // error: NoSuchElementException

    // firstOrNull() with a lambda predicate to return null if the predicate is not satisfied
    println(emptyList.firstOrNull { it > 50 }) // null
    println(emptySet.firstOrNull { it > 50 }) // null

    // lastOrNull() with a lambda predicate to return null if the predicate is not satisfied
    println(emptyList.lastOrNull { it > 50 }) // null
    println(emptySet.lastOrNull { it > 50 }) // null
    
    // using find() with a lambda predicate 
    println(numberList.find { it > 5 }) // 6
    println(numberSet.find { it > 5 }) // 6
    
    // using findLast() with a lambda predicate
    println(numberList.findLast { it > 5 }) // 10
    println(numberSet.findLast { it > 5 }) // 10
    
    // using find() and findLast() with a lambda to return null if the predicate is not satisfied
    println(emptyList.findLast { it > 50 }) // null
    println(emptySet.findLast { it > 50 }) // null
}

Retrieve with selector

Sometimes, it is helpful to use a selector to map the collection before getting an element. The firstNotNullOf() method performs the following actions: maps the collection with a selector function and returns the first non-null value in the result. However, it throws NoSuchElementException if there is no non-null element. To avoid that, you can use firstNotNullOfOrNull().

fun main() {
    
    val listOfNames = listOf("John", "Jane", "Mary", "Peter", "John", "Jane", "Mary", "Peter")
    
    // Mapping and returning a list of values
    println(
        listOfNames.firstNotNullOf { item -> item.uppercase().takeIf { it.length >= 4 } }
    ) //JOHN
    /*
    println(
        listOfNames.firstNotNullOf { item -> item.uppercase().takeIf { it.length >= 10 } }
    ) // Exception
    */
    println(
        listOfNames.firstNotNullOfOrNull { item -> item.uppercase().takeIf { it.length >= 4 } }
    ) //JOHN
    println(
        listOfNames.firstNotNullOfOrNull { item -> item.uppercase().takeIf { it.length >= 10 } }
    ) // null

}

Random element

Oftentimes, what you need is to obtain a random value from a collection. To do that, we can use the random() function. Remember, though, that this function throws NoSuchElementException if the array is empty. To avoid that, you can use the safer call randomOrNull(), which returns null if the collection is empty.

fun main() {
    
    val listOfNames = listOf("John", "Jane", "Mary", "Peter", "John", "Jane", "Mary", "Peter")
    
    // returning a random element
    println(listOfNames.random()) // Peter
    println(listOfNames.randomOrNull()) // Jane
    
    val emptyListNames = listOf<String>()
    // println(emptyListNames.random()) // Exception
    println(emptyListNames.randomOrNull()) // null

}

Check if an element exists

One of the crucial and daily tasks you will face when working with collections is to check if an element or a set of elements exists.
To do that, you can use the contains() method – this method returns true if the element is found in the collection. You can also do it in a more idiomatic way in Kotlin with the operator in. To check if a collection contains multiple elements, you can make use of the method containsAll().

Also, you can check if the collection has any elements with isEmpty() (it returns true if the collection is empty) or isNotEmpty() (it returns true if the collection is not empty).

fun main() {

    val listOfNames = listOf("John", "Jane", "Mary", "Peter", "John", "Jane", "Mary", "Peter")
    val emptyListNames = listOf<String>()

    // contains() returns true if the element is present in the list
    println(listOfNames.contains("John")) // true
    println(listOfNames.contains("john")) // false
    
    // the in operator can also be used to check if an element is present in the list
    println("John" in listOfNames) // true
    println("john" in listOfNames) // false
    
    // containsAll() returns true if all the elements are present in the list
    println(listOfNames.containsAll(listOf("John", "Jane"))) // true
    println(listOfNames.containsAll(listOf("John", "Jane", "john"))) // false

    // list is empty
    println(listOfNames.isEmpty()) // false
    println(emptyListNames.isEmpty()) // true
    
    // list is not empty
    println(listOfNames.isNotEmpty()) // true
    println(emptyListNames.isNotEmpty()) // false


}

Conclusion

In this topic, you have explored different ways of getting elements from a collection: by indicating the position, using conditions, with a selector, or randomly. Also, you have learned how to check if an element or several elements are contained in a collection.

Now, you are going to put it all into practice. Are you ready for the challenge?

77 learners liked this piece of theory. 2 didn't like it. What about you?
Report a typo