Overview of Kotlin Collections API

1. Overview

In this quick tutorial, we’ll introduce the Kotlin’s Collections API, and we’ll discuss the different collection types in Kotlin and some common operations on collections.

2. Collection vs. Mutable Collection

First, let’s take a look at different types of collections in Kotlin. We will see how to initialize basic types of collections.

The Collection interface supports read-only methods while MutableCollection support read/write methods.

2.1. List

We can create a simple read-only List using method listOf() and read-write MutableList using mutableListOf():

val theList = listOf("one", "two", "three")

val theMutableList = mutableListOf("one", "two", "three")

2.2. Set

Similarly we can create a read-only Set using method setOf() and read-write MutableSet using mutableSetOf():

val theSet = setOf("one", "two", "three")

val theMutableSet = mutableSetOf("one", "two", "three")

2.3. Map

We can also create a read-only Map using method mapOf() and read-write MutableMap using mutableMapOf():

val theMap = mapOf(1 to "one", 2 to "two", 3 to "three")

val theMutableMap = mutableMapOf(1 to "one", 2 to "two", 3 to "three")

3. Useful Operators

Kotlin’s Collections API is much richer than the one we can find in Java – it comes with a set of overloaded operators.

3.1. The “in” Operator

We can use expression “x in collection” which can be translated to collection.contains(x):

@Test
fun whenSearchForExistingItem_thenFound () {
    val theList = listOf("one", "two", "three")

    assertTrue("two" in theList)
}

3.2. The “+” Operator

[[the—​operator]]We can an element or entire collection to another using “+” operator:

@Test
fun whenJoinTwoCollections_thenSuccess () {
    val firstList = listOf("one", "two", "three")
    val secondList = listOf("four", "five", "six")
    val resultList = firstList + secondList

    assertEquals(6, resultList.size)
    assertTrue(resultList.contains("two"))
    assertTrue(resultList.contains("five"))
}

3.3. The “-“ Operator

Similarly, we can remove an element or multiple elements using “-” operator:

@Test
fun whenExcludeItems_thenRemoved () {
    val firstList = listOf("one", "two", "three")
    val secondList = listOf("one", "three")
    val resultList = firstList - secondList

    assertEquals(1, resultList.size)
    assertTrue(resultList.contains("two"))
}

4. Other Methods

Finally, we will explore some common methods for collection. In Java, if we wanted to leverage advanced methods, we would need to use Stream API.

In Kotlin, we can find similar methods available in the Collections API.

We can obtain a sublist from a given List:

@Test
fun whenSliceCollection_thenSuccess () {
    val theList = listOf("one", "two", "three")
    val resultList = theList.slice(1..2)

    assertEquals(2, resultList.size)
    assertTrue(resultList.contains("two"))
}

We can easily remove all nulls from a List:

@Test
fun whenFilterNullValues_thenSuccess () {
    val theList = listOf("one", null, "two", null, "three")
    val resultList = theList.filterNotNull()

    assertEquals(3, resultList.size)
}

We can filter collection items easily using filter(), which works similarly to the filter() method from Java Stream API:

@Test
fun whenFilterNonPositiveValues_thenSuccess () {
    val theList = listOf(1, 2, -3, -4, 5, -6)
    val resultList = theList.filter{ it > 0}

    assertEquals(3, resultList.size)
    assertTrue(resultList.contains(1))
    assertFalse(resultList.contains(-4))
}

We can drop first N items:

@Test
fun whenDropFirstItems_thenRemoved () {
    val theList = listOf("one", "two", "three", "four")
    val resultList = theList.drop(2)

    assertEquals(2, resultList.size)
    assertFalse(resultList.contains("one"))
    assertFalse(resultList.contains("two"))
}

We can drop items first few items if they satisfy the given condition:

@Test
fun whenDropFirstItemsBasedOnCondition_thenRemoved () {
    val theList = listOf("one", "two", "three", "four")
    val resultList = theList.dropWhile{ it.length < 4 }

    assertEquals(2, resultList.size)
    assertFalse(resultList.contains("one"))
    assertFalse(resultList.contains("two"))
}

We can group elements:

@Test
fun whenGroupItems_thenSuccess () {
    val theList = listOf(1, 2, 3, 4, 5, 6)
    val resultMap = theList.groupBy{ it % 3}

    assertEquals(3, resultMap.size)

    assertTrue(resultMap[1]!!.contains(1))
    assertTrue(resultMap[2]!!.contains(5))
}

We can map all elements using the provided function:

@Test
fun whenApplyFunctionToAllItems_thenSuccess () {
    val theList = listOf(1, 2, 3, 4, 5, 6)
    val resultList = theList.map{ it * it }

    assertEquals(4, resultList[1])
    assertEquals(9, resultList[2])
}

We can use flatmap() to flatten nested collections. Here, we are converting Strings to List<String> and avoiding ending up with List<List<String>>:

@Test
fun whenApplyMultiOutputFunctionToAllItems_thenSuccess () {
    val theList = listOf("John", "Tom")
    val resultList = theList.flatMap{ it.toLowerCase().toList() }

    assertEquals(7, resultList.size)
}

We can perform fold/reduce operation:

@Test
fun whenApplyFunctionToAllItemsWithStartingValue_thenSuccess () {
    val theList = listOf(1, 2, 3, 4, 5, 6)
    val finalResult = theList.fold(0, {acc, i -> acc + (i * i)})

    assertEquals(91, finalResult)
}

5. Conclusion

We explored Kotlin’s Collections API and some of the most interesting methods.

And, as always, the full source code can be found over on GitHub.

Leave a Reply

Your email address will not be published.